I made a little script for custom encryption and decryption algorithms, which are based on Caesar cipher and transcription cipher.
First a little bit of background for explanation on how it should work. Assume a message you want to encrypt before sending. You apply Caesar cipher to it, which is shift each letter by 3 positions in alphabet to right. Then you take that encrypted text and divide it to sections by number of letters your key has. Let's say your message has n letters, and your key has m letters. Then your message should be broken up to n/m sections (rounding up or ceiling), each one of m letters. If the message length isn't divisible by the number of letters in key, then it has to be padded to fill in missing letters by letter A. Then given by the key, you transpose the columns based on the position of that particular letter of key corresponding to column number in alphabet. So if your key is let's say LEAK, then columns have to be transposed based on the position as follows: 3241, because 1234 corresponds to AEKL (sorted by alphabetical order). Then this transposed matrix is then concatenated back to the corresponding string, which then can be sent. Decipher is basically the same process reversed in order → You receive a message, and you know the key. You parcel it to columns and then transpose the columns in order to correspond to alphabetical order of the letters from key, concatenate it back, and then run through reverse Caesar cipher (shift letters by 3 positions in alphabet to the left). You may get a few X letters at the end, that doesn't actually matter.
Now to my issue: I wrote a little Javascript program for it, it is in fact a module, and I also have a test script for it to see how it works. But it doesn't work correcly. The transposition is wrong, which results in deciphered text being malformed. I know the deciphered text as I've done this algorithm manually before writing that script and I know the outcome it should have.
This is the module:
module.exports = {
decrypt: (message, key) => {
if(message.length % key.length != 0) {
throw new Error(`Lenght of message is not divisible by lenght of the key! ${message.length} is not divisible by ${key.length}!`);
}
let key_array_unsorted = key.split('');
let key_array_sorted = key.split('').sort();
let message_matrix = [];
for(i = 0; i < message.length / key.length; ++i) {
let quartet = [];
for(j = 0; j < key.length; ++j) {
quartet.push(message.charAt(i*key.length + j));
}
message_matrix.push(quartet);
}
let message_matrix_shuffled = [];
message_matrix.forEach(quartet => {
let quartet_shuffled = [];
for(i = 0; i < key.length; ++i) {
for(j = 0; j < key.length; ++j) {
if(key_array_unsorted[i] == key_array_sorted[j]) {
quartet_shuffled.push(quartet[j]);
}
}
}
message_matrix_shuffled.push(quartet_shuffled);
});
let message_quartets = [];
message_matrix_shuffled.forEach(quartet => {message_quartets.push(quartet.join(''))});
let message_caesar = message_quartets.join('');
let message_deciphered = "";
for(i = 0; i < message_caesar.length; ++i) {
let charcode = message_caesar.charCodeAt(i);
let alphanum = charcode - 65;
let alphanum_new = (alphanum + 23) % 26;
let charcode_new = alphanum_new + 65;
message_deciphered += String.fromCharCode(charcode_new);
}
return message_deciphered;
},
encrypt: (message, key) => {
let message_caesar = "";
for(i = 0; i < message.length; ++i) {
let charcode = message.charCodeAt(i);
let alphanum = charcode - 65;
let alphanum_new = (alphanum + 3) % 26;
let charcode_new = alphanum_new + 65;
message_caesar += String.fromCharCode(charcode_new);
}
for(i = 0; i <= Math.ceil(message_caesar.length / key.length) * key.length - message_caesar.length; ++i) {
message_caesar += "A";
}
let key_array_unsorted = key.split('');
let key_array_sorted = key.split('').sort();
let message_matrix = [];
for(i = 0; i < message_caesar.length / key.length; ++i) {
let quartet = [];
for(j = 0; j < key.length; ++j) {
quartet.push(message_caesar.charAt(i*key.length + j));
}
message_matrix.push(quartet);
}
let message_matrix_shuffled = [];
message_matrix.forEach(quartet => {
let quartet_shuffled = [];
for(i = 0; i < key.length; ++i) {
for(j = 0; j < key.length; ++j) {
if(key_array_sorted[i] == key_array_unsorted[j]) {
quartet_shuffled.push(quartet[j]);
}
}
}
message_matrix_shuffled.push(quartet_shuffled);
});
let message_quartets = [];
message_matrix_shuffled.forEach(quartet => {message_quartets.push(quartet.join(''))});
let message_ciphered = message_quartets.join('');
return message_ciphered;
}
}
And this is the test script:
const cipher = require('./cipher');
let message = "HGYRDGQCREGLDYQROXRHABAK";
let key = "OMYL";
console.log(`Received message: ${message}`);
console.log(`Known passphrase: ${key}`);
let message_deciphered = cipher.decrypt(message, key);
console.log(`Deciphered message: ${message_deciphered}`);
let message_encrypted = cipher.encrypt(message_deciphered, key);
console.log(`Control encryption: ${message_encrypted}`);
The module and the test scripts are in the same folder.
This is how the output should have been:
Received message: HGYRDGQCREGLDYQROXRHABAK
Known passphrase: OMYL
Deciphered message: ODEVZDANIBODOVANEULOHYXX
Control encryption: HGYRDGQCREGLDYQROXRHABAK
And this is current output:
Received message: HGYRDGQCREGLDYQROXRHABAK
Known passphrase: OMYL
Deciphered message: VDOENDZADBIONVOAOUELXYHX
Control encryption: HGYRDGQCREGLDYQROXRHABAK
Now, I know for sure that the key's letters are sorted correctly and that's not the cause of the incorrect transcription, I actually looked for the key's letters being sorted correctly and it's okay. OMYL does in fact sort correctly to LMOY.
The issue has to be in the transcription itself, where it doesn't correspond to the alphabetical order of the key. The thing is, I can't for some reason spot a mistake, if there is any. I guess Javascript isn't really much good language to use for such thing, but it is fairly easy and doesn't require compilation. All I wanted to do is make myself a gadget or tool to make quick solver for my assignment, which I get in Monday and I already know the topic for that assignment, which is that cipher.
Maybe I am doing it wrong, maybe I shouldn't actually use the key letters to be compared and instead build a sort of shuffle map for the transcription based on the alphabetical order of the key letters (and of course the order of steps - encryption or decryption). Which is something I don't know how to do, unfortunately :(
Based on the algorithm given by the OP in the chat, the code word sequencing is wrong
Compare sorted sequence with unsorted sequence instead of the other way
Replace key_array_unsorted[i] == key_array_sorted[j] with key_array_sorted[i] == key_array_unsorted[j]
Related
we are in our 3 week of our Bootcamp.
Project is to code the Hangman game, it is running in the terminal.
We got preety far in just 2 days I think.
We are now stucked on the case insensesetivity, meaning if the word we are looking for is Java that if you type j or J, you still would get the same result J_ _ _.
I looked all over the Internet and found the locale.compare method which isn´t working for me. If i type a wrong letter everything is fine but if I type a right one I get a error and also it doesn´t ignore the case of the letter.
We are not using functions yet because we haven´t learned how they work.
const constants = require('./constants');
// In node.js: install a prompt library by running: `npm install prompt-sync` in the current folder
const prompt = require("prompt-sync")();
// Here you see an example how to get your
// constants from constants.js
/*for(let figure of constants.HANGMAN_PICS)
{
console.log(figure);
}
*/
let Answer = [];
var Words = constants.WORDS_TO_GUESS[Math.floor(Math.random() * constants.WORDS_TO_GUESS.length)];
/*console.log(Words.length); //Wortlänge von random Word
*/
for (let i = 0; i < Words.length; i++) {
Answer[i] = "_";
}
console.log(Answer.join(" "));
for (; Answer !== Words;) {
input = prompt(`Finde das Wort.`);
if (Words.includes(input)) {
for (let i = 0; i < Words.length; i++) {
if (Words[i] === input) {
Answer[i] = input;
}
}
console.log(Answer.join(" "));
console.log(Answer.localeCompare(Words, {
sensitivity: `base`
}));
} else if (!Words.includes(input)) {
console.log("Falsche Eingabe - keep trying!");
console.log(Answer.join(" "))
}
}
// how to use the prompt - e.g.:
// const name = prompt('What is your name?');
my way of doing this (which may not be the best) is setting both strings to lower case and comparing them with includes() function.
The for loop afterwards will just collect right position/s that you want to show up after right guess.
const word = 'JavaScript'
const char = 's' //input
const indexes = [] //output
//https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
//includes() function
if(word.toLowerCase().includes(char.toLowerCase())){
//if you need to get the index/es of the latter/s
for (let i = 0; i < word.toLowerCase().length; i++) {
if (word.toLowerCase()[i] === char.toLowerCase()) {
indexes.push(i)
}
}
}
console.log(indexes)
Make sure that Words (which should be word, singular and not capitalized) is in lower case (or upper case) by doing .toLocaleLowerCase() (or .toLocaleUpperCase()) at the end of where you assign it:
var Words = constants.WORDS_TO_GUESS[Math.floor(Math.random() * constants.WORDS_TO_GUESS.length)].toLocaleLowerCase();
// >>> −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^
Then make sure input is lower case (or upper case):
input = prompt(`Finde das Wort.`).toLocaleLowerCase();
At that point, you'll be comparing j and j (or J and J), so you'll find a match.
If you don't want to consistently use one capitalization in your code, it's possible to do it differently, but more complicated.
Basically, you do the .toLocaleLowerCase() (or .toLocaleUpperCase()) everywhere you do a comparison:
for (; Answer !== Words;) {
input = prompt(`Finde das Wort.`).toLocaleLowerCase();
// Get the input in lower case −−−−−−^^^^^^^^^^^^^^^^^^^^
if (Words.toLocaleLowerCase().includes(input)) {
// ^^^^^^^^^^^^^^^^^^^^−−− make Words lower case for the includes call
for (let i = 0; i < Words.length; i++) {
if (Words[i].toLocaleLowerCase() === input) {
// ^^^^^^^^^^^^^^^^^^^^−− make Words[i] lower case for the ===
Answer[i] = Words[i];
// ^^^^^^^^−−− use the one from Words, not the user's input
}
}
// ...
}
}
I'm completely stuck here. Here's the problem I'm solving:
A shift cipher takes a plain text message and shifts each letter forward in the alphabet by a given number. For example, a shift cipher with a shift of 1 would turn the string 'hello' to 'ifmmp'.
Example:
const cipher = new ShiftCipher(2);
cipher.encrypt('I love to code!'); // returns 'K NQXG VQ EQFG!'
cipher.decrypt('K <3 OA RWRRA'); // returns 'i <3 my puppy'
Here's my code:
// Write class below
class ShiftCipher {
constructor(num) {
this.num = num;
}
encrypt(str) {
const lowCase = str.toLowerCase();
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
let encrypted = '';
for(let i = 0; i < lowCase.length; i++) {
if(!alphabet.includes(lowCase[i])) {
encrypted += lowCase[i];
}
for(let j = 0; j < alphabet.length; j++) {
if(lowCase[i] == alphabet[j]) {
encrypted += alphabet[j + this.num].toUpperCase();
}
}
}
console.log(encrypted);
}
}
const cipher = new ShiftCipher(2);
cipher.encrypt('I love to codez!');
Last two lines is for testing my method. Everything works fine until iteration doesn't come to an end of alphabet. I added z to the end to test it working with last letter of alphabet. It should start over from alphabet so when I call Z with 2 it should return B but of course I get error because string ends and I'm unable to make it start over.
I've googled around and understand that continue might be solution here but I have still failed to use it in my code.
Cheers!
The problem seems to be this line:
encrypted += alphabet[j + this.num].toUpperCase();
When you reach the end of the alphabet, the letter z, j + this.num will be greater than the length of the alphabet. Indexing the array out of range will produce undefined so your code breaks.
Add a wrap-around like this:
encrypted += alphabet[(j + this.num) % alphabet.length].toUpperCase();
I know that there is a lot of information on here about regex's, but I really cant seem to get this to work. I have a for loop, looping through an array. I want to see if the current index of the array is not equal to a group of numbers (32-64). I have declared a variable let patt which holds the regex that I think should work, but I cant figure out the syntax to check against it. I was sure it would be .match, but again, not sure how to word !.match
any advise, solutions or even a point in the direction of a good JS regex tutorial would be much appreciated!
class ShiftCipher{
constructor(shift){
this.shift = shift;
}
encrypt(string){
let up = string.toUpperCase(); //convert string to uppercase
let uni = [];
let newArr = [];
let i, j;
let patt = /[32-64]/g; //think this is wrong...
for(i = 0; i < up.length; i++){
uni.push(up.charCodeAt(i)) //push converted chars as unicodes to new array
if(uni[i] != 32){ // if unicode is 32 (space) leave as is. //I want a regex here for != unicode 32 - 64
uni[i] += this.shift; // shift unicode by parent class specification (shift)
}
}
for(j = 0; j < up.length; j++){
if(uni[j] > 90){ // if unicode is higher than 90(z)..
uni[j] -= 26; // loop back round starting at (a).
}
let text = String.fromCharCode(uni[j]); //convert from unicode to string
newArr.push(text); //push string to array
}
let final = newArr.join(''); //join array elements(as a string) and store in final
console.log(final);
}
}
const cipher = new ShiftCipher(2);
cipher.encrypt('I love to code z!');
I want to see if the current index of the array is not equal to a group of numbers (32-64)
Char codes are numbers. Try numerical comparisons.
for (i = 0; i < up.length; i++) {
if (up.charCodeAt(i) >= 32 && up.charCodeAt(i) <= 64) {
// ...
}
}
But technically, you can use regex too. Creating a character range from char codes works like this:
var patt = /[\x20-\x40]/; // hex 20 = decimal 32, hex 40 == decimal 64
for (i = 0; i < up.length; i++) {
if (patt.test(up.charAt(i)) {
// ...
}
}
Note that this uses .charAt().
The /[\x20-\x40]/ is interpreted as if you had written the actual characters, so in this case it's equivalent to /[ -#]/.
As your wraparound constant is 26, the length of the English alphabet, one could assume that you want to alter only the letters, and then the magical regexp is [A-Z]:
class ShiftCipher {
constructor(shift) {
this.shift = shift;
}
encrypt(string) {
return string.toUpperCase().replaceAll(/[A-Z]/g, char => {
let code = char.charCodeAt(0) + this.shift;
if(code>90)
code-=26;
return String.fromCharCode(code);
});
}
}
const cipher = new ShiftCipher(2);
console.log(cipher.encrypt('I love to code z!'));
class VigenèreCipher {
constructor(key, abc) {
this.encode = function (str) {
//split the string into an array
let arr = abc.split("");
let keyValue = [];
for (let i = 0; i < key.length; i++) {
//finding the letter(key) value and push it to key value
keyValue.push(arr.indexOf(key[i]));
}
let redacted = "";
let pointer = 0;
for (let i = 0; i < str.length; i++) {
if (arr.includes(str[i])) {
let shift = arr.indexOf(str[i]) + keyValue[pointer];
if (shift >= 26) {
redacted += arr[shift - 26];
} else {
redacted += arr[shift];
}
//debugging code
console.log(
`string: ${str[i]}`,
`shift: ${shift - 26}`,
`shiftTo ${arr[shift]}`,
`pointer: ${pointer}`,
`KeyValue: ${keyValue[pointer]}`
);
pointer += 1;
} else {
pointer = 0;
redacted += str[i];
}
if (pointer >= keyValue.length) {
pointer = 0;
}
}
//console.log(keyValue);
console.log(`redacted: ${redacted}`);
return redacted
};
this.decode = function (str) {
let arr = abc.split("");
let keyValue = [];
for (let i = 0; i < key.length; i++) {
//finding the letter(key) value and push it to key value
keyValue.push(arr.indexOf(key[i]));
}
let decoded = "";
let pointer = 0;
for (let i = 0; i < str.length; i++) {
if (arr.includes(str[i])) {
let shift = arr.indexOf(str[i]) - keyValue[pointer];
if (shift < 0) {
decoded += arr[26 - Math.abs((arr.indexOf(str[i]) - keyValue[pointer]))];
} else {
decoded += arr[Math.abs((arr.indexOf(str[i]) - keyValue[pointer]))];
}
//debugging code
console.log(
`string: ${str[i]} `,
`shift: ${arr.indexOf(str[i]) - keyValue[pointer]} `,
`shiftTo ${arr[arr.indexOf(str[i]) - keyValue[pointer]]} `,
`pointer: ${pointer} `,
`KeyValue: ${keyValue[pointer]} `,
`arrindex: ${arr.indexOf(str[i])} `,
);
pointer += 1;
} else {
pointer = 0;
decoded += str[i];
}
if (pointer >= keyValue.length) {
pointer = 0;
}
}
//console.log(keyValue);
console.log(`decode: ${decoded}`);
return decoded
};
};
}
The Vigenère cipher is a method of encrypting alphabetic text by using a series of different
Caesar ciphers based on the letters of a keyword. It is a simple form of polyalphabeticsubstitution.
In a Caesar cipher, each letter of the alphabet is shifted along some number of places; for example,in a Caesar cipher of shift 3, A would become D, B would become E, Y would become B and so on. The Vigenère cipher consists of several Caesar ciphers in sequence with different shift values. Assume the key is repeated for the length of the text, character by character. Note that some implementations repeat the key over characters only if they are part of the alphabet -- this is not the case here.The shift is derived by applying a Caesar shift to a character with the corresponding index of the key in the alphabet.
**ar abc, key;
abc = "abcdefghijklmnopqrstuvwxyz";
key = "password"
c = new VigenèreCipher(key, abc);
Test.assertEquals(c.encode('codewars'), 'rovwsoiv');
Test.assertEquals(c.decode('rovwsoiv'), 'codewars');
Test.assertEquals(c.encode('waffles'), 'laxxhsj');
Test.assertEquals(c.decode('laxxhsj'), 'waffles');
Test.assertEquals(c.encode('CODEWARS'), 'CODEWARS');
Test.assertEquals(c.decode('CODEWARS'), 'CODEWARS');**
**Expected: 'xt\'k o vwixl qzswej!', instead got: 'xt\'h p hhaxp rihzaf!'
Expected: 'it\'s a shift cipher!', instead got: 'it\'v z gwqfp bzaeiv!'**
**Expected: 'ドオカセガヨゴザキアニ', instead got: 'ドテタヒガォゴザキノイ'
Expected: 'ドモアリガトゴザイマス', instead got: 'ドタシェガホゴザイィス'**
**The code solve most problem but won't solve those any idea whats going on or how to fix it?**
The information given in the post states
... Note that some implementations repeat the key over characters only if they are part of the alphabet -- this is not the case here. ...
To achieve this you increment the pointer and/or set it to zero after processing each and every character. You could put the unconditional update of pointer immediately before the end of loop, removing other pointer updates, like
...
pointer += 1;
if( pointer >= keyValue.length) {
pointer = 0
}
} // end of for loop
or by calculating it directly as
pointer = (pointer + 1) % keyValue.length;
This gets the code to encode "it's a shift cipher!" as "xt'k o vwixl qzswej!" as expected, and decode that result back to the original without error (after fixing pointer updating in both encode and decode of course).
You reset the pointer to zero in the else when you detect a character that is not in the alphabet. In general, you only increase the "pointer" within the key if you encrypt and otherwise you leave it alone, you don't reset it.
I'm attempting to teach myself javascript. I chose something I assumed was simple, but ran into problems relatively quickly.
I'm attempting to search a string for another string given by the user.
My code so far is:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = [];
var one = 0;
var two = 0;
var k = 0;
var sourceSearch = function(text) {
for(i = 0; i < source.length; i++) { //for each character in the source
if(source[i] === searchString[0]) { //if a character in source matches the first element in the users input
one = source.indexOf(i); //confused from here on
for(p = searchString.length; p > 0; p--) {
}
}
}
};
sourceSearch(searchString);
My idea was:
check to see if the first loop finds a character that matches the first character in the user input
if it matches, check to see if the next X characters after the first match the next X characters in the source string
if they all match, push them to the hits array
My problem: I have no idea how to iterate along the arrays without nesting quite a few if statements, and even then, that wouldn't be sufficient, considering I want the program to work with any input.
Any ideas would be helpful. Thanks very much in advance.
Note: There are a few un-used variables from ideas I was testing, but I couldn't make them work.
You can try:
if (source.indexOf(searchString) !== -1) {
// Match!
}
else
{
//No Match!
}
As the other answers so far point out, JavaScript strings have an indexOf function that does what you want. If you want to see how it's done "by hand", you can modify your function like this:
var sourceSearch = function(text) {
var i, j, ok; // always declare your local variables. globals are evil!
// for each start position
for(i = 0; i < source.length; i++) {
ok = true;
// check for a match
for (j = searchString.length - 1; ok && j >= 0; --j) {
ok = source[i + j] === searchString[j];
}
if (ok) {
// searchString found starting at index i in source
}
}
};
This function will find all positions in source at which searchString was found. (Of course, you could break out of the loop on the first success.) The logic is to use the outer loop to advance to each candidate start position in source and use the inner loop to test whether that position actually is the position of a match to searchString.
This is not the best algorithm for searching strings. The built-in algorithm is much faster (both because it is a better algorithm and because it is native code).
to follow your approach, you can just play with 2 indexes:
var sourceSearch = function(text) {
j = 0;
for(i = 0; i < source.length; i++) {
if(source[i] === text[j]) {
j++;
} else {
j = 0;
}
if (j == text.length) {
console.log(i - j); //this prints the starting index of the matching substring
}
}
};
These answers are all pretty good, but I'd probably opt for something like this:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = source.split(searchString);
var hitsCount = hits.length - 1;
This way you have all of the data you need to figure out where each hit occurred in he source, if that's important to you.