Skip to content

Commit 65be90b

Browse files
committed
Complete cipher algo
1 parent 26cef2e commit 65be90b

File tree

2 files changed

+272
-0
lines changed

2 files changed

+272
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Mật mã hàng rào đường sắt
2+
3+
**Mật mã hàng rào đường sắt** (còn được gọi là **mật mã zigzag**) là [mật mã chuyển vị](https://en.wikipedia.org/wiki/Transposition_cipher) trong đó thông điệp được chia thành một tập hợp các đường ray trên hàng rào để mã hóa. Hàng rào được điền các ký tự của thông điệp, bắt đầu từ trên cùng bên trái, và thêm các ký tự kế tiếp theo đường chéo xuống, đến xuống cùng thì hướng lên đầu ray theo chuyển động zig-zag. Lặp đi lặp lại cho đến khi thông điệp được lắp đầy trong hàng rào. Thông điệp được mã hoá là kết quả của việc nối văn bản trong mỗi thanh ray, từ trên xuống dưới.
4+
5+
Từ [wikipedia](https://en.wikipedia.org/wiki/Rail_fence_cipher), ta có thông điệp là `WE ARE DISCOVERED. FLEE AT ONCE` trên một hàng rào `3` ray:
6+
7+
```
8+
W . . . E . . . C . . . R . . . L . . . T . . . E
9+
. E . R . D . S . O . E . E . F . E . A . O . C .
10+
. . A . . . I . . . V . . . D . . . E . . . N . .
11+
-------------------------------------------------
12+
WECRLTEERDSOEEFEAOCAIVDEN
13+
```
14+
15+
Sau đó, thông điệp có thể được giải mã bằng cách tạo lại hàng rào được mã hóa, với cùng một mẫu truyền tải, ngoại trừ các ký tự chỉ nên được thêm vào một đường ray tại một thời điểm.
16+
17+
Để minh họa điều đó, một dấu gạch ngang có thể được thêm vào các đường ray tại các vị trí chưa được điền. Như vậy hàng rào sẽ trông như thế này sau khi điền vào đường ray đầu tiên, các dấu gạch ngang đại diện cho các vị trí đã được truy cập nhưng chưa có giá trị.
18+
19+
```
20+
W . . . E . . . C . . . R . . . L . . . T . . . E
21+
. - . - . - . - . - . - . - . - . - . - . - . - .
22+
. . - . . . - . . . - . . . - . . . - . . . - . .
23+
```
24+
25+
Đã đến lúc bắt đầu điền vào đường ray tiếp theo khi số lượng vị trí hàng rào được truy cập bằng số ký tự trong thông điệp.
26+
27+
## Liên kết
28+
29+
- [Rail Fence Cipher on Wikipedia](https://en.wikipedia.org/wiki/Rail_fence_cipher)
30+
- [Rail Fence Cipher Calculator](https://crypto.interactive-maths.com/rail-fence-cipher.html)
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/**
2+
* @typedef {string[]} Rail
3+
* @typedef {Rail[]} Fence
4+
* @typedef {number} Direction
5+
*/
6+
7+
/**
8+
* @constant DIRECTIONS
9+
* @type {object}
10+
* @property {Direction} UP
11+
* @property {Direction} DOWN
12+
*/
13+
const DIRECTIONS = { UP: -1, DOWN: 1 };
14+
15+
/**
16+
* Tạo hàng rào với số hàng cụ thể.
17+
*
18+
* @param {number} rowsNum
19+
* @returns {Fence}
20+
*/
21+
const buildFence = (rowsNum) => Array(rowsNum)
22+
.fill(null)
23+
.map(() => []);
24+
25+
/**
26+
* Nhận hướng tiếp theo để di chuyển (dựa trên hướng hiện tại) khi truy cập hàng rào.
27+
*
28+
* @param {object} params
29+
* @param {number} params.railCount - Số hàng trong hàng rào.
30+
* @param {number} params.currentRail - Hàng hiện tại đang truy cập.
31+
* @param {Direction} params.direction - Hướng hiện tại.
32+
* @returns {Direction} - Hướng tiếp theo để thực hiện.
33+
*/
34+
const getNextDirection = ({ railCount, currentRail, direction }) => {
35+
switch (currentRail) {
36+
case 0:
37+
// Đi xuống nếu ta đang ở trên của hàng rào.
38+
return DIRECTIONS.DOWN;
39+
case railCount - 1:
40+
// Đi lên nếu ta đang ở dưới của hàng rào.
41+
return DIRECTIONS.UP;
42+
default:
43+
// Tiếp tục hướng hiện tại nếu ta đang ở giữa hàng rào.
44+
return direction;
45+
}
46+
};
47+
48+
/**
49+
* @param {number} targetRailIndex
50+
* @param {string} letter
51+
* @returns {Function}
52+
*/
53+
const addCharToRail = (targetRailIndex, letter) => {
54+
/**
55+
* Cho một đường ray, thêm một ký tự vào nó nếu nó khớp với targetIndex.
56+
*
57+
* @param {Rail} rail
58+
* @param {number} currentRail
59+
* @returns {Rail}
60+
*/
61+
function onEachRail(rail, currentRail) {
62+
return currentRail === targetRailIndex
63+
? [...rail, letter]
64+
: rail;
65+
}
66+
return onEachRail;
67+
};
68+
69+
/**
70+
* Điền các ký từ vào hàng rào.
71+
*
72+
* @param {object} params
73+
* @param {Fence} params.fence
74+
* @param {number} params.currentRail
75+
* @param {Direction} params.direction
76+
* @param {string[]} params.chars
77+
* @returns {Fence}
78+
*/
79+
const fillEncodeFence = ({
80+
fence,
81+
currentRail,
82+
direction,
83+
chars,
84+
}) => {
85+
if (chars.length === 0) {
86+
// Tất cả ký tự đều nằm trong hàng rào.
87+
return fence;
88+
}
89+
90+
const railCount = fence.length;
91+
92+
// Lấy ký tự tiếp theo điền vào hàng rào.
93+
const [letter, ...nextChars] = chars;
94+
const nextDirection = getNextDirection({
95+
railCount,
96+
currentRail,
97+
direction,
98+
});
99+
100+
return fillEncodeFence({
101+
fence: fence.map(addCharToRail(currentRail, letter)),
102+
currentRail: currentRail + nextDirection,
103+
direction: nextDirection,
104+
chars: nextChars,
105+
});
106+
};
107+
108+
/**
109+
* @param {object} params
110+
* @param {number} params.strLen
111+
* @param {string[]} params.chars
112+
* @param {Fence} params.fence
113+
* @param {number} params.targetRail
114+
* @param {Direction} params.direction
115+
* @param {number[]} params.coords
116+
* @returns {Fence}
117+
*/
118+
const fillDecodeFence = (params) => {
119+
const {
120+
strLen, chars, fence, targetRail, direction, coords,
121+
} = params;
122+
123+
const railCount = fence.length;
124+
125+
if (chars.length === 0) {
126+
return fence;
127+
}
128+
129+
const [currentRail, currentColumn] = coords;
130+
const shouldGoNextRail = currentColumn === strLen - 1;
131+
const nextDirection = shouldGoNextRail
132+
? DIRECTIONS.DOWN
133+
: getNextDirection(
134+
{ railCount, currentRail, direction },
135+
);
136+
const nextRail = shouldGoNextRail ? targetRail + 1 : targetRail;
137+
const nextCoords = [
138+
shouldGoNextRail ? 0 : currentRail + nextDirection,
139+
shouldGoNextRail ? 0 : currentColumn + 1,
140+
];
141+
142+
const shouldAddChar = currentRail === targetRail;
143+
const [currentChar, ...remainderChars] = chars;
144+
const nextString = shouldAddChar ? remainderChars : chars;
145+
const nextFence = shouldAddChar ? fence.map(addCharToRail(currentRail, currentChar)) : fence;
146+
147+
return fillDecodeFence({
148+
strLen,
149+
chars: nextString,
150+
fence: nextFence,
151+
targetRail: nextRail,
152+
direction: nextDirection,
153+
coords: nextCoords,
154+
});
155+
};
156+
157+
/**
158+
* @param {object} params
159+
* @param {number} params.strLen
160+
* @param {Fence} params.fence
161+
* @param {number} params.currentRail
162+
* @param {Direction} params.direction
163+
* @param {number[]} params.code
164+
* @returns {string}
165+
*/
166+
const decodeFence = (params) => {
167+
const {
168+
strLen,
169+
fence,
170+
currentRail,
171+
direction,
172+
code,
173+
} = params;
174+
175+
if (code.length === strLen) {
176+
return code.join('');
177+
}
178+
179+
const railCount = fence.length;
180+
181+
const [currentChar, ...nextRail] = fence[currentRail];
182+
const nextDirection = getNextDirection(
183+
{ railCount, currentRail, direction },
184+
);
185+
186+
return decodeFence({
187+
railCount,
188+
strLen,
189+
currentRail: currentRail + nextDirection,
190+
direction: nextDirection,
191+
code: [...code, currentChar],
192+
fence: fence.map((rail, idx) => (idx === currentRail ? nextRail : rail)),
193+
});
194+
};
195+
196+
/**
197+
* Mã hoá bằng Mật mã hàng rào đường sắt.
198+
*
199+
* @param {string} string - chuỗi cần mã hoá.
200+
* @param {number} railCount - Số lượng đường ray trong hàng rào.
201+
* @returns {string} - Chuỗi đã mã hoá.
202+
*/
203+
export const encodeRailFenceCipher = (string, railCount) => {
204+
const fence = buildFence(railCount);
205+
206+
const filledFence = fillEncodeFence({
207+
fence,
208+
currentRail: 0,
209+
direction: DIRECTIONS.DOWN,
210+
chars: string.split(''),
211+
});
212+
213+
return filledFence.flat().join('');
214+
};
215+
216+
/**
217+
* Giải mã bằng Mật mã hàng rào đường sắt.
218+
*
219+
* @param {string} string - Chuỗi đã mã hoá.
220+
* @param {number} railCount - Số lượng đường ray trong hàng rào.
221+
* @returns {string} - Chuỗi đã giải mã.
222+
*/
223+
export const decodeRailFenceCipher = (string, railCount) => {
224+
const strLen = string.length;
225+
const emptyFence = buildFence(railCount);
226+
const filledFence = fillDecodeFence({
227+
strLen,
228+
chars: string.split(''),
229+
fence: emptyFence,
230+
targetRail: 0,
231+
direction: DIRECTIONS.DOWN,
232+
coords: [0, 0],
233+
});
234+
235+
return decodeFence({
236+
strLen,
237+
fence: filledFence,
238+
currentRail: 0,
239+
direction: DIRECTIONS.DOWN,
240+
code: [],
241+
});
242+
};

0 commit comments

Comments
 (0)