Add double stepping, re-work component files, fix bugs.
This commit is contained in:
		
							
								
								
									
										4
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					# prettier insists on making the array literals very long and thin. Aside from taking up
 | 
				
			||||||
 | 
					# space, it also makes it hard to tell, at a glance, if the array contains 26
 | 
				
			||||||
 | 
					# characters. I've opted to do my own little formatting in this file.
 | 
				
			||||||
 | 
					components.ts
 | 
				
			||||||
							
								
								
									
										187
									
								
								src/components.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/components.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					export type RotorComponent = {
 | 
				
			||||||
 | 
						name: M3RotorName | M4RotorName,
 | 
				
			||||||
 | 
						values: string[],
 | 
				
			||||||
 | 
						turnovers: string[],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ReflectorComponent = {
 | 
				
			||||||
 | 
						name: M3ReflectorName | M4ReflectorName,
 | 
				
			||||||
 | 
						reflector: string[],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type M3RotorName = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII";
 | 
				
			||||||
 | 
					export type M3ReflectorName = "A" | "B" | "C";
 | 
				
			||||||
 | 
					export type M4RotorName = "Beta" | "Gamma";
 | 
				
			||||||
 | 
					export type M4ReflectorName = "B Thin" | "C Thin";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const m3Rotots: Record<M3RotorName, RotorComponent> = {
 | 
				
			||||||
 | 
						I: {
 | 
				
			||||||
 | 
							name: "I",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"E", "K", "M", "F", "L", "G",
 | 
				
			||||||
 | 
								"D", "Q", "V", "Z", "N", "T",
 | 
				
			||||||
 | 
								"O", "W", "Y", "H", "X", "U",
 | 
				
			||||||
 | 
								"S", "P", "A", "I", "B", "R",
 | 
				
			||||||
 | 
								"C", "J",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["R"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						II: {
 | 
				
			||||||
 | 
							name: "II",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"A", "J", "D", "K", "S", "I",
 | 
				
			||||||
 | 
								"R", "U", "X", "B", "L", "H",
 | 
				
			||||||
 | 
								"W", "T", "M", "C", "Q", "G",
 | 
				
			||||||
 | 
								"Z", "N", "P", "Y", "F", "V",
 | 
				
			||||||
 | 
								"O", "E",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["F"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						III: {
 | 
				
			||||||
 | 
							name: "III",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"B", "D", "F", "H", "J", "L", 
 | 
				
			||||||
 | 
								"C", "P", "R", "T", "X", "V",
 | 
				
			||||||
 | 
								"Z", "N", "Y", "E", "I", "W",
 | 
				
			||||||
 | 
								"G", "A", "K", "M", "U", "S",
 | 
				
			||||||
 | 
								"Q", "O",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["W"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						IV: {
 | 
				
			||||||
 | 
							name: "IV",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"E", "S", "O", "V", "P", "Z",
 | 
				
			||||||
 | 
								"J", "A", "Y", "Q", "U", "I",
 | 
				
			||||||
 | 
								"R", "H", "X", "L", "N", "F",
 | 
				
			||||||
 | 
								"T", "G", "K", "D", "C", "M",
 | 
				
			||||||
 | 
								"W", "B",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["K"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						V: {
 | 
				
			||||||
 | 
							name: "V",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"V", "Z", "B", "R", "G", "I",
 | 
				
			||||||
 | 
								"T", "Y", "U", "P", "S", "D",
 | 
				
			||||||
 | 
								"N", "H", "L", "X", "A", "W",
 | 
				
			||||||
 | 
								"M", "J", "Q", "O", "F", "E",
 | 
				
			||||||
 | 
								"C", "K",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["A"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						VI: {
 | 
				
			||||||
 | 
							name: "VI",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"J", "P", "G", "V", "O", "U",
 | 
				
			||||||
 | 
								"M", "F", "Y", "Q", "B", "E",
 | 
				
			||||||
 | 
								"N", "H", "Z", "R", "D", "K",
 | 
				
			||||||
 | 
								"A", "S", "X", "L", "I", "C",
 | 
				
			||||||
 | 
								"T", "W",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["A", "N"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						VII: {
 | 
				
			||||||
 | 
							name: "VII",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"N", "Z", "J", "H", "G", "R",
 | 
				
			||||||
 | 
								"C", "X", "M", "Y", "S", "W",
 | 
				
			||||||
 | 
								"B", "O", "U", "F", "A", "I",
 | 
				
			||||||
 | 
								"V", "L", "P", "E", "K", "Q",
 | 
				
			||||||
 | 
								"D", "T",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["A", "N"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						VIII: {
 | 
				
			||||||
 | 
							name: "VIII",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"F", "K", "Q", "H", "T", "L",
 | 
				
			||||||
 | 
								"X", "O", "C", "B", "J", "S",
 | 
				
			||||||
 | 
								"P", "D", "Z", "R", "A", "M",
 | 
				
			||||||
 | 
								"E", "W", "N", "I", "U", "Y",
 | 
				
			||||||
 | 
								"G", "V",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: ["A", "N"],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const m3Reflectors: Record<M3ReflectorName, ReflectorComponent> = {
 | 
				
			||||||
 | 
						A: {
 | 
				
			||||||
 | 
							name: "A",
 | 
				
			||||||
 | 
							reflector: [
 | 
				
			||||||
 | 
								"E", "J", "M", "Z", "A", "L",
 | 
				
			||||||
 | 
					 			"Y", "X", "V", "B", "W", "F",
 | 
				
			||||||
 | 
					 			"C", "R", "Q", "U", "O", "N",
 | 
				
			||||||
 | 
					 			"T", "S", "P", "I", "K", "H",
 | 
				
			||||||
 | 
					 			"G", "D",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						B: {
 | 
				
			||||||
 | 
							name: "B",
 | 
				
			||||||
 | 
							reflector: [
 | 
				
			||||||
 | 
								"Y", "R", "U", "H", "Q", "S",
 | 
				
			||||||
 | 
					 			"L", "D", "P", "X", "N", "G",
 | 
				
			||||||
 | 
					 			"O", "K", "M", "I", "E", "B",
 | 
				
			||||||
 | 
					 			"F", "Z", "C", "W", "V", "J",
 | 
				
			||||||
 | 
					 			"A", "T",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						C: {
 | 
				
			||||||
 | 
							name: "C",
 | 
				
			||||||
 | 
							reflector: [
 | 
				
			||||||
 | 
								"F", "V", "P", "J", "I", "A",
 | 
				
			||||||
 | 
					 			"O", "Y", "E", "D", "R", "Z",
 | 
				
			||||||
 | 
					 			"X", "W", "G", "C", "T", "K",
 | 
				
			||||||
 | 
					 			"U", "Q", "S", "B", "N", "M",
 | 
				
			||||||
 | 
					 			"H", "L",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const m4Rotors: Record<M4RotorName, RotorComponent> = {
 | 
				
			||||||
 | 
						Beta: {
 | 
				
			||||||
 | 
							name: "Beta",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"L", "E", "Y", "J", "V", "C",
 | 
				
			||||||
 | 
					 			"N", "I", "X", "W", "P", "B",
 | 
				
			||||||
 | 
					 			"Q", "M", "D", "R", "T", "A",
 | 
				
			||||||
 | 
					 			"K", "Z", "G", "F", "U", "H",
 | 
				
			||||||
 | 
					 			"O", "S",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: [],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						Gamma: {
 | 
				
			||||||
 | 
							name: "Gamma",
 | 
				
			||||||
 | 
							values: [
 | 
				
			||||||
 | 
								"F", "S", "O", "K", "A", "N",
 | 
				
			||||||
 | 
					 			"U", "E", "R", "H", "M", "B",
 | 
				
			||||||
 | 
					 			"T", "I", "Y", "C", "W", "L",
 | 
				
			||||||
 | 
					 			"Q", "P", "Z", "X", "V", "G",
 | 
				
			||||||
 | 
					 			"J", "D",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							turnovers: [],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const m4Reflectors: Record<M4ReflectorName, ReflectorComponent> = {
 | 
				
			||||||
 | 
						"B Thin": {
 | 
				
			||||||
 | 
							name: "B Thin",
 | 
				
			||||||
 | 
							reflector: [
 | 
				
			||||||
 | 
								"E", "N", "K", "Q", "A", "U",
 | 
				
			||||||
 | 
					 			"Y", "W", "J", "I", "C", "O",
 | 
				
			||||||
 | 
					 			"P", "B", "L", "M", "D", "X",
 | 
				
			||||||
 | 
					 			"Z", "V", "F", "T", "H", "R",
 | 
				
			||||||
 | 
					 			"G", "S",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"C Thin": {
 | 
				
			||||||
 | 
							name: "C Thin",
 | 
				
			||||||
 | 
							reflector: [
 | 
				
			||||||
 | 
								"R", "D", "O", "B", "J", "N",
 | 
				
			||||||
 | 
					 			"T", "K", "V", "E", "H", "M",
 | 
				
			||||||
 | 
					 			"L", "F", "C", "W", "Z", "A",
 | 
				
			||||||
 | 
					 			"X", "G", "Y", "I", "P", "S",
 | 
				
			||||||
 | 
					 			"U", "Q",
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}as const;
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/index.ts
									
									
									
									
									
								
							@ -1,6 +1,11 @@
 | 
				
			|||||||
import { Machine } from "./machine";
 | 
					import { Machine } from "./machine";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const machine = new Machine("A", "I", "II", "III");
 | 
					let machine = new Machine("A", "I", "II", "III");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.log(machine.input("THISISAMESSAGEFORYOU"));
 | 
					console.log("THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOG");
 | 
				
			||||||
console.log("rings", machine.rings());
 | 
					
 | 
				
			||||||
 | 
					const cipher = machine.input("THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOG");
 | 
				
			||||||
 | 
					console.log(cipher);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					machine = new Machine("A", "I", "II", "III");
 | 
				
			||||||
 | 
					console.log(machine.input(cipher));
 | 
				
			||||||
 | 
				
			|||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
import fs from "node:fs";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type RotorValues = {
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
	values: string[];
 | 
					 | 
				
			||||||
	turnovers: string[];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type ReflectorValues = {
 | 
					 | 
				
			||||||
	name: string;
 | 
					 | 
				
			||||||
	reflector: Map<string, string>;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function loadFile(filename: string) {
 | 
					 | 
				
			||||||
	const data = fs.readFileSync(filename, "utf-8");
 | 
					 | 
				
			||||||
	let row: string[] = [];
 | 
					 | 
				
			||||||
	const table: string[][] = [];
 | 
					 | 
				
			||||||
	let cell = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (const char of data) {
 | 
					 | 
				
			||||||
		if (char !== "," && char !== "\n") {
 | 
					 | 
				
			||||||
			cell += char;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			row.push(cell);
 | 
					 | 
				
			||||||
			cell = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (char === "\n") {
 | 
					 | 
				
			||||||
				table.push(row);
 | 
					 | 
				
			||||||
				row = [];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// if the file ends in a line break, this isn't needed but if it doesn't, then there
 | 
					 | 
				
			||||||
	// is a rotor hanging out at the end that still needs to be added.
 | 
					 | 
				
			||||||
	if (row.length > 0) {
 | 
					 | 
				
			||||||
		row.push(cell);
 | 
					 | 
				
			||||||
		table.push(row);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return table;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function buildRotors(filename: string) {
 | 
					 | 
				
			||||||
	const rotors: RotorValues[] = [];
 | 
					 | 
				
			||||||
	const table = loadFile(filename);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (const [name, valueString, turnovers] of table) {
 | 
					 | 
				
			||||||
		rotors.push({
 | 
					 | 
				
			||||||
			name,
 | 
					 | 
				
			||||||
			values: valueString.split(""),
 | 
					 | 
				
			||||||
			turnovers: turnovers.split(""),
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return rotors;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function buildReflectors(filename: string) {
 | 
					 | 
				
			||||||
	const reflectors: ReflectorValues[] = [];
 | 
					 | 
				
			||||||
	const table = loadFile(filename);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (const [name, valueString] of table) {
 | 
					 | 
				
			||||||
		const values = valueString.split("");
 | 
					 | 
				
			||||||
		const reflector = { name, reflector: new Map() };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (let i = 0; i < values.length; i++) {
 | 
					 | 
				
			||||||
			const a = String.fromCharCode("A".charCodeAt(0) + i);
 | 
					 | 
				
			||||||
			const b = values[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			reflector.reflector.set(a, b);
 | 
					 | 
				
			||||||
			reflector.reflector.set(b, a);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reflectors.push(reflector);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return reflectors;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										109
									
								
								src/machine.ts
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								src/machine.ts
									
									
									
									
									
								
							@ -1,29 +1,67 @@
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
	buildReflectors,
 | 
						M3ReflectorName,
 | 
				
			||||||
	buildRotors,
 | 
						m3Reflectors,
 | 
				
			||||||
	ReflectorValues,
 | 
						M3RotorName,
 | 
				
			||||||
	RotorValues,
 | 
						m3Rotots as m3Rotors,
 | 
				
			||||||
} from "./loadComponentData";
 | 
						M4ReflectorName,
 | 
				
			||||||
 | 
						m4Reflectors,
 | 
				
			||||||
 | 
						M4RotorName,
 | 
				
			||||||
 | 
						m4Rotors,
 | 
				
			||||||
 | 
					} from "./components";
 | 
				
			||||||
import { Plugboard } from "./plugboard";
 | 
					import { Plugboard } from "./plugboard";
 | 
				
			||||||
import { Rotor } from "./rotors";
 | 
					import { Rotor } from "./rotors";
 | 
				
			||||||
import { checkCharacter } from "./validation";
 | 
					import { checkCharacter } from "./validation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rotors = buildRotors("rotors.csv");
 | 
					 | 
				
			||||||
const reflectors = buildReflectors("reflectors.csv");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class Machine {
 | 
					export class Machine {
 | 
				
			||||||
	plugboard: Plugboard;
 | 
						plugboard: Plugboard;
 | 
				
			||||||
	reflector: ReflectorValues;
 | 
						reflector: Map<string, string>;
 | 
				
			||||||
	rotors: Rotor[];
 | 
						rotors: Rotor[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(reflector: string, first: string, second: string, third: string) {
 | 
						constructor(
 | 
				
			||||||
 | 
							reflector: M3ReflectorName,
 | 
				
			||||||
 | 
							first: M3RotorName,
 | 
				
			||||||
 | 
							second: M3RotorName,
 | 
				
			||||||
 | 
							third: M3RotorName,
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						constructor(
 | 
				
			||||||
 | 
							reflector: M4ReflectorName,
 | 
				
			||||||
 | 
							first: M3RotorName,
 | 
				
			||||||
 | 
							second: M3RotorName,
 | 
				
			||||||
 | 
							third: M3RotorName,
 | 
				
			||||||
 | 
							fourth: M4RotorName,
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						constructor(
 | 
				
			||||||
 | 
							reflector: M3ReflectorName | M4ReflectorName,
 | 
				
			||||||
 | 
							first: M3RotorName,
 | 
				
			||||||
 | 
							second: M3RotorName,
 | 
				
			||||||
 | 
							third: M3RotorName,
 | 
				
			||||||
 | 
							fourth?: M4RotorName,
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
		this.plugboard = new Plugboard();
 | 
							this.plugboard = new Plugboard();
 | 
				
			||||||
		this.reflector = getComponent(reflectors, reflector);
 | 
							const reflectorComponent = getComponent(
 | 
				
			||||||
 | 
								{ ...m3Reflectors, ...m4Reflectors },
 | 
				
			||||||
 | 
								reflector,
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.rotors = [];
 | 
							this.reflector = new Map();
 | 
				
			||||||
		this.rotors.push(getRotor(getComponent(rotors, first)));
 | 
							for (let i = 0; i < 26; i++) {
 | 
				
			||||||
		this.rotors.push(getRotor(getComponent(rotors, second)));
 | 
								const aCharCode = "A".charCodeAt(0);
 | 
				
			||||||
		this.rotors.push(getRotor(getComponent(rotors, third)));
 | 
								this.reflector.set(
 | 
				
			||||||
 | 
									String.fromCharCode(aCharCode + i),
 | 
				
			||||||
 | 
									reflectorComponent.reflector[i],
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const rotorComponents = [
 | 
				
			||||||
 | 
								getComponent(m3Rotors, first),
 | 
				
			||||||
 | 
								getComponent(m3Rotors, second),
 | 
				
			||||||
 | 
								getComponent(m3Rotors, third),
 | 
				
			||||||
 | 
							];
 | 
				
			||||||
 | 
							if (fourth) rotorComponents.push(getComponent(m4Rotors, fourth));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.rotors = rotorComponents.map(
 | 
				
			||||||
 | 
								({ name, values, turnovers }) => new Rotor(name, values, turnovers),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rings(): string[] {
 | 
						rings(): string[] {
 | 
				
			||||||
@ -40,20 +78,42 @@ export class Machine {
 | 
				
			|||||||
			checkCharacter(char, "character");
 | 
								checkCharacter(char, "character");
 | 
				
			||||||
			let turnover = true;
 | 
								let turnover = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// There is a rare case when rotor three turns, and it does not cause rotor
 | 
				
			||||||
 | 
								// two to turn (double-step): on the FIRST keypress when BOTH wheels are set
 | 
				
			||||||
 | 
								// to turn. In that case, it's important not to set the second wheel as the
 | 
				
			||||||
 | 
								// third one steps, since it will already have stepped and stepping twice in
 | 
				
			||||||
 | 
								// one keypress is not possible mechanically.
 | 
				
			||||||
 | 
								let rotorTwoStepped = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// plugboard, first pass
 | 
								// plugboard, first pass
 | 
				
			||||||
			let scrambled = this.plugboard.in(char);
 | 
								let scrambled = this.plugboard.in(char);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// rotors, first pass (in)
 | 
								// rotors, first pass (in)
 | 
				
			||||||
			for (const rotor of this.rotors) {
 | 
								for (let i = 0; i < this.rotors.length; i++) {
 | 
				
			||||||
 | 
									const rotor = this.rotors[i];
 | 
				
			||||||
				if (turnover) {
 | 
									if (turnover) {
 | 
				
			||||||
					turnover = rotor.rotate();
 | 
										turnover = rotor.rotate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (i === 1) {
 | 
				
			||||||
 | 
											rotorTwoStepped = true;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Because of a quirk in the machines design, if the third rotor
 | 
				
			||||||
 | 
										// rotates, the second rotor also rotates. This funny action was
 | 
				
			||||||
 | 
										// called "double-stepping". Although Naval Enigma added a fourth
 | 
				
			||||||
 | 
										// rotor, it never causes the third rotor to double-step because
 | 
				
			||||||
 | 
										// the fourth rotor never actually rotated. Rotor one is not
 | 
				
			||||||
 | 
										// affected because it always rotates anyway.
 | 
				
			||||||
 | 
										if (i === 2 && !rotorTwoStepped) {
 | 
				
			||||||
 | 
											this.rotors[1].rotate();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				scrambled = rotor.in(scrambled);
 | 
									scrambled = rotor.in(scrambled);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// reflector
 | 
								// reflector
 | 
				
			||||||
			scrambled = this.reflector.reflector.get(scrambled)!;
 | 
								scrambled = this.reflector.get(scrambled)!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// rotors, second pass (out)
 | 
								// rotors, second pass (out)
 | 
				
			||||||
			for (let i = this.rotors.length - 1; i >= 0; i--) {
 | 
								for (let i = this.rotors.length - 1; i >= 0; i--) {
 | 
				
			||||||
@ -63,22 +123,23 @@ export class Machine {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			// plugboard, second pass
 | 
								// plugboard, second pass
 | 
				
			||||||
			output += this.plugboard.in(scrambled);
 | 
								output += this.plugboard.in(scrambled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let state = "";
 | 
				
			||||||
 | 
								for (const r of this.rotors) {
 | 
				
			||||||
 | 
									state += ` ${r.name}, ${r.offset}`;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return output;
 | 
							return output;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getRotor({ name, values, turnovers }: RotorValues) {
 | 
					 | 
				
			||||||
	return new Rotor(name, values, turnovers, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function getComponent<T extends { name: string }>(
 | 
					function getComponent<T extends { name: string }>(
 | 
				
			||||||
	components: T[],
 | 
						components: Record<string, T>,
 | 
				
			||||||
	componentName: string,
 | 
						componentName: string,
 | 
				
			||||||
): T {
 | 
					): T {
 | 
				
			||||||
	const component = components.find(({ name }) => name === componentName);
 | 
						const component = components[componentName];
 | 
				
			||||||
	if (!component) throw new Error(`unknown reflector: ${component}`);
 | 
						if (!component) throw new Error(`unknown component: ${component}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return component;
 | 
						return component;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ export class Plugboard {
 | 
				
			|||||||
		this.links.set(b, a);
 | 
							this.links.set(b, a);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unlink(a: string) {
 | 
						unstecker(a: string) {
 | 
				
			||||||
		checkCharacter(a, "a");
 | 
							checkCharacter(a, "a");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!this.links.has(a)) return;
 | 
							if (!this.links.has(a)) return;
 | 
				
			||||||
 | 
				
			|||||||
@ -27,12 +27,12 @@ export class Rotor {
 | 
				
			|||||||
			throw new Error("offset must be an integer");
 | 
								throw new Error("offset must be an integer");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const char = this.values[this.offset];
 | 
					 | 
				
			||||||
		const turnover = this.turnovers.includes(char);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.offset += times;
 | 
							this.offset += times;
 | 
				
			||||||
		this.offset %= 26;
 | 
							this.offset %= 26;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const char = String.fromCharCode("A".charCodeAt(0) + this.offset);
 | 
				
			||||||
 | 
							const turnover = this.turnovers.includes(char);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return turnover;
 | 
							return turnover;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,16 +45,17 @@ export class Rotor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		const posNum = pos.charCodeAt(0);
 | 
							const posNum = pos.charCodeAt(0);
 | 
				
			||||||
		const input = (posNum - "A".charCodeAt(0) + this.offset) % 26;
 | 
							const input = (posNum - "A".charCodeAt(0) + this.offset) % 26;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return this.values[input];
 | 
							return this.values[input];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	out(pos: string): string {
 | 
						out(pos: string): string {
 | 
				
			||||||
		checkCharacter(pos, "position");
 | 
							checkCharacter(pos, "position");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const i = this.values.findIndex((char) => char === pos);
 | 
							let i = this.values.findIndex((char) => char === pos);
 | 
				
			||||||
		const str = String.fromCharCode(
 | 
							i -= this.offset;
 | 
				
			||||||
			"A".charCodeAt(0) + ((i + this.offset) % 26),
 | 
							if (i < 0) i = 26 + i;
 | 
				
			||||||
		);
 | 
							const str = String.fromCharCode("A".charCodeAt(0) + i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return str;
 | 
							return str;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user