Caesar Cipher in p5js - javascript

I'm a super noob, and I'm trying to make a Caesar cipher in p5js, so far I manage to code the UI, but now I'm stuck and don't really know how to move forward can someone please help?
I know I need to use for loops, but I can't figure out how?
I really appreciate all the help
Thanks
let inp;
let button;
let alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
function setup() {
createCanvas(600, 600);
// Type here your plain or encryted message
inp = createInput();
inp.position(20, 30);
inp.size(550, 200);
// Encrypted / Decrypted message
inp = createInput();
inp.position(20, 340);
inp.size(550, 200);
// Key
inp = createInput();
inp.position(20, 280);
inp.size(200, 20);
button = createButton("Encrypt");
button.position(370, 260);
button.size(100, 50);
// button.mousePressed(encrypt);
button = createButton("Decrypt");
button.position(475, 260);
button.size(100, 50);
// button.mousePressed(decrypt);
noStroke();
// button.mousePressed(drawName);
}
function draw() {
background(0)
text("Type here your plain or encryted message", 20, 20);
text("Encrypted / Decrypted message", 20, 330);
text("Key", 20, 270);
fill(255)
}

Here's a link to the completed version: https://editor.p5js.org/Samathingamajig/sketches/7P5e__R8M
But I'll actually explain what I did so that you gain something from this.
How a Caesar Cipher works:
Start with a seed value, and integer of how much to rotate the text
To encrypt, for each letter add the seed to the letter. i.e. A + 2 = C, B + 5 = G, etc.
To decrypt, for each letter subtract the seed from the letter. i.e. C - 2 = A, G - 5 = B, etc.
Pseudo code:
function encrypt():
initial text = (get initial text)
output = "" // empty string
offset (aka seed) = (get the seed, make sure its an integer) mod 26
for each character in initial:
if character is in the alphabet:
index = (find the index of the character in the alphabet)
outputIndex = (index + offset + 26 /* this 26 isn't needed, but it's there to make the decrypt be a simple copy + paste, you'll see */ ) mod 26 /* force between 0 and 25, inclusive */
output += outputIndex to character
else:
output += initial character
(set the output text field to the output)
function decrypt():
initial text = (get initial text)
output = "" // empty string
offset (aka seed) = (get the seed, make sure its an integer)
for each character in initial:
if character is in the alphabet:
index = (find the index of the character in the alphabet)
outputIndex = (index - offset + 26 /* when subtracting the offset, the character could be a negative index */ ) mod 26 /* force between 0 and 25, inclusive */
output += outputIndex to character
else:
output += initial character
(set the output text field to the output)
Some other changes to your code:
Some other changes I made to your code that are required for this are:
Have a global array of all buttons, and a global array of all inputs. You kept overriding the value so there was only one reference at a time, so you weren't able to access the values in the encrypt and decrypt functions
Changed the order of creating the inputs (text fields), so that they are defined vertically, this is good for when we push to the array
Made the alphabet variable a string rather than an array, you can still do indexOf(), includes(), alphabet[i], etc. with a string and the definition looks cleaner

Related

How to distribute one array of elements over another but have the element count gradually go down?

I'm creating mock data for my app with fakerJS.
I have created one array of 10 000 user IDs. And 1 array of 100 000 content IDs.
I want to distribute these content IDs over the user ID array where the first one will get the most content IDs and the last user will get the least (0).
Eg.
const userIds = ['a', 'b', 'c', ...] // 10_000 long
const contentIds = ['c1', 'c2', 'c3', ...] // 100_000 long
const result = distribute(userIds, contentIds)
result // { a: ['c1', 'c2', 'c3', ...], b: [...], z: [] }
The distribution should look something like this theoretically:
However, the 40 here for highest number is way too low, so imagine this being way higher for the first user ID and with 10_000 users, many of the last users could have 0 content IDs with this distribution basically.
I've been coding for 6+ years, but I think I need to start learning Mathematics to figure this one out, would appreciate any help on how to even start 😅
Maybe a logarithm decay and truncate (you can play with value_data.push formula until you find what you want)
Start and End
0: 90
1: 83
2: 79
3: 76
4: 74
5: 72
...
9995: 0
9996: 0
9997: 0
9998: 0
9999: 0
<script>
var time_data = [];
var value_data = [];
max = 10000;
//logarithmic decay array and truncate
for (let i=1;i<=max;++i) {
time_data.push(i);
value_data.push( Math.floor(100 - ( (9.71672 * (1 + Math.log(i)) ) )) );
}
//initializer
guarda = 0;
valueGuarda = 0;
for (let index = 0; index < time_data.length; index++) {
guarda = time_data[index] + guarda;
valueGuarda = value_data[index] + valueGuarda;
}
//arrays
console.log(time_data)
console.log(value_data)
//calculating the summation
let total = value_data.reduce((a, b) => a + b, 0); //100000
console.log("total: " + total);
</script>

JavaScript Regex to find UOM in a string

I have a list of products that contains UOM in the product title. It needs automatically detect the UOM in the title by using Regex.
Expectations
Banana Yogurt 70ml returns ml
Fish Nuggets 200G returns g
Potato Wedges 200 G returns g
I have this function below
detectMetricUnit = (title) => {
let unit,
regex = new RegExp(/(?:\d)/mg),
measurement = title.match(regex) && title.match(regex)[0],
matches = measurement && title.split(measurement)[1];
if(matches) {
if(/millilitre|milliliter|ml/.test(matches.toLowerCase())){
unit = 'ml';
} else if(/litre|liter|l/.test(matches.toLowerCase())){
unit = 'l';
} else if (/kilogram|kg/.test(matches.toLowerCase())) {
unit = 'kg';
} else if (/gram|g/.test(matches.toLowerCase())) {
unit = 'g';
}
}
return unit;
}
However I have some problematic strings such as
Chocolate Drink 330ML X 24 matches 3 and return null UOM
which I am expecting to get ml.
Appreciate if someone could point out my mistake in my regex. How do I actually get the full integers and find the UOM attached next to it even with a space?
You may define a dictionary of possible UOMs you want to detect and then build a regex similar to
/(\d+(?:\.\d+)?)\s?(millilitre|milliliter|ml|litre|liter|l|kilogram|kg|gram|g)\b/i
See the regex demo. The (\d+(?:\.\d+)?) part will capture an integer or float value into Group 1, then \s? match an optional whitespace (change to \s* to match 0 or more whitespaces), and then (millilitre|milliliter|ml|litre|liter|l|kilogram|kg|gram|g)\b will capture UOM unit into Group 2 as a whole word (due to \b word boundary).
Here is the JS implementation to get the first UOM from string:
let strs = ['Banana Yogurt 70ml', 'Fish Nuggets 200G', 'Potato Wedges 200 G', 'Chocolate Drink 330ML X 24']
let dct = {millilitre: 'ml', milliliter: 'ml', ml: 'ml', litre:'l', liter: 'l', l: 'l', kilogram: 'kg', kg: 'kg', gram: 'g', g: 'g'}
detectMetricUnit = (title) => {
let unit, match, val,
regex = new RegExp("(\\d+(?:\\.\\d+)?)\\s?(" + Object.keys(dct).join("|") + ")\\b", "i");
match = title.match(regex);
if (match) {
val = match[1];
unit = dct[match[2].toLowerCase()]
}
return [val, unit];
}
strs.forEach(x => console.log(detectMetricUnit(x)) )
To get all of them, multiple occurrences:
let strs = ['Banana Yogurt 70ml and Fish Nuggets 200G', 'Potato Wedges 200 G and Chocolate Drink 330ML X 24']
let dct = {millilitre: 'ml', milliliter: 'ml', ml: 'ml', litre:'l', liter: 'l', l: 'l', kilogram: 'kg', kg: 'kg', gram: 'g', g: 'g'}
detectMetricUnit = (title) => {
let match, results = [],
regex = new RegExp("(\\d+(?:\\.\\d+)?)\\s?(" + Object.keys(dct).join("|") + ")\\b", "ig");
while (match=regex.exec(title)) {
results.push([ match[1], dct[match[2].toLowerCase()] ]);
}
return results;
}
strs.forEach(x => console.log(x, detectMetricUnit(x)) )

Keep getting undefined when calling 2 dimensional array in JS

So I'm currently making a bit of script that will take in a value between 0 and 3999 and pump out the roman numerals for the number. For some reason when calling my 2 dimensional array, I wind up getting undefined
function romanConverter() {
var romanOutput;
var enteredNum = prompt('What number would you like converted between 1 and 3999?');
var romanNum = [
['', 'M', 'MM', 'MMM'], // Thousands place
['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'], // Hundreds place
['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'], // These are for the tens place
['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'] // Ones place
];
if (parseInt(enteredNum) > 3999) {
alert("I'm sorry that's not a valid entry");
enteredNum = prompt('What number would you like converted between 1 and 3999?');
} else {
while (enteredNum.length < 4) {
enteredNum = '0' + enteredNum;
}
for (var i = 0; i <= enteredNum.length; i += 1) {
var currentNum = parseInt(enteredNum.charAt(i));
romanOutput += romanNum[i][currentNum];
}
}
document.write(romanOutput);
}
romanConverter();
I always get the TypeError: romanNum[i] is undefined I'm really stuck and could use some help.
This is one of those sneaky little errors that always makes you bang your head against a wall for a while.
Try changing your final for loop to the following:
for (var i = 0; i < enteredNum.length; i += 1) { // etc...
You want strictly less than - not less than or equal to. Otherwise, you'll end up evaluating something like "05".charAt(2);
To prevent its output from being undefined followed by the converted roman numeral, you will also want to change the line that says
var romanOutput;
To
var romanOutput = "";

5-second count down timer

I have a simple question. I'm trying to add a a count down timer to my first ever project. I'm planning on putting the timer on the top of the page, and I want it to display the current time left starting from 5:00 to 0:00. I know how to do it using setInterval which could do something like time-- every second, but I want it to also display the current milliseconds and I'm not sure how to go about doing that. Any help would be very much appreciated!
This is the code I've written so far, and right now it just has a 5 second timeout used in the assignWords() method.
(function () {
'use strict';
// Dictionary object used to manipulate anagrams
var dictionary = {};
// List of letters used to help create incorrect choices
dictionary.letters = [
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x',
'y', 'z'];
// List of words that are used for anagram questions
dictionary.words = [
'adaxial', 'agreeably', 'antinoise', 'asthenia', 'astint', 'babushka', 'bailiffry', 'bathtub', 'bestab', 'bestiary', 'bibulous', 'bordage', 'bostonite', 'brogue', 'brushoff', 'budlet', 'cathepsin', 'centesimi', 'chaste', 'chicayote', 'coastal', 'coppice', 'couple', 'cuapinole', 'cytoplasm', 'daubingly', 'dearth', 'deasil', 'drightin', 'drudge', 'ejecta', 'feelable', 'fistnote', 'flareback', 'folial', 'fortunate', 'garrulous', 'gemmology', 'glaringly', 'gleet', 'globule', 'gluepot', 'googol', 'googul', 'humuslike', 'ichnology', 'illiberal', 'issite', 'karyotin', 'kella', 'ketol', 'knowingly', 'lysogenic', 'macaque', 'meddle', 'menseful', 'mocha', 'mournival', 'musher', 'natty', 'nonactive', 'nonserous', 'outcut', 'outspeak', 'overheavy', 'partially', 'pernor', 'picnic', 'prickwood', 'pyorrheal', 'redly', 'refine', 'regaler', 'rollick', 'sandling', 'sarcastic', 'scypha', 'severely', 'sinkage', 'sissyish', 'sogging', 'staling', 'steellike', 'stonelike', 'stoneware', 'tadpolism', 'tarditude', 'tazia', 'thymiosis', 'tightener', 'tritical', 'trundler', 'undenuded', 'underbank', 'unpaining', 'untraded', 'wayfare', 'woodworm', 'woofer', 'zemeism'];
// Stores the count of the remaining words
dictionary.wordCount = dictionary.words.length;
/*
* Returns a random letter from dictionary.letters
*/
dictionary.randLetter = function () {
return this.letters[Math.floor(Math.random() * 100 % 26)];
};
/*
* Replaces one letter of a word with a randomly selected letter
*/
dictionary.replaceLetter = function (word) {
var index = Math.floor(Math.random() * 100 % word.length);
var newWord = word.slice(0, index) + word.slice(index + 1);
return newWord += this.randLetter();
};
/*
* Returns a random word from dictionary.words
*/
dictionary.randWord = function () {
return this.words[Math.floor(Math.random() * 100 % this.wordCount)];
};
/*
* Randomly shuffles the letters around in a word
*/
dictionary.shuffle = function (word) {
var fragments = word.split('');
for (var i = fragments.length; i > 0;) {
var random = parseInt(Math.random() * i);
var temp = fragments[--i];
fragments[i] = fragments[random];
fragments[random] = temp;
}
return fragments.join('');
};
/*
* Returns the correct answer for the current word
*/
dictionary.getCorrectChoice = function (word) {
return this.shuffle(word);
};
/*
* Returns an incorrect answer for the current word
*/
dictionary.getIncorrectChoice = function (word) {
word = this.replaceLetter(word);
return this.shuffle(word);
};
/*
* Randomly assigns the current word and correct and incorrect choices to the four buttons
*/
function assignWords() {
// Clear the timeout for the previous question
window.clearTimeout(dictionary.timeout);
// Allow 5 seconds for the user to get the right answer
dictionary.timeout = window.setTimeout(function() {
alert('you lose!');
}, 5000);
var currWord = document.getElementById('currentWord');
var buttons = document.getElementsByTagName('button');
// Randomly choose a word to use as the anagram test
currWord.innerHTML = dictionary.randWord();
// Randomly choose a button to hold the correct choice
dictionary.correctButton = buttons[Math.floor(Math.random() * 4)];
dictionary.correctButton.innerHTML = dictionary.getCorrectChoice(currWord.innerHTML);
// Give the rest of the buttons incorrect choices
for (var i = 0; i < buttons.length; i++) {
if (buttons[i] === dictionary.correctButton) {
continue;
} else {
buttons[i].innerHTML = dictionary.getIncorrectChoice(currWord.innerHTML);
}
}
}
// View object used to change information displayed on the page
var view = {};
// Stores the player's current score
view.score = 0;
// The timer that has the remaining time to answer the question
view.timer = 0;
//
view.resetData = function() {
};
view.displayData = function() {
assignWords();
var buttons = document.getElementsByTagName('button');
for(var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function(event) {
if (event.target === dictionary.correctButton) {
assignWords();
}
});
}
};
view.displayData();
})();
Use setInterval with a small interval such as 50ms to update your timer display.
To get the time left, instead of decrementing a counter by 1 every second, just note the start time and subtract it from the current time. This will give you the total elapsed milliseconds since the timer started which you can then format for display.
Have found similar code on the W3C website and thought it might be what you are looking for:
var d = new Date();
var x = document.getElementById("demo");
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
var ms = d.getMilliseconds();
x.innerHTML = h + ":" + m + ":" + s + ":" + ms;

How to convert human readable memory size into bytes?

I'm trying to convert strings that match /(\d)+(\.\d+)?(m|g|t)?b?/i into bytes.
For example, 1KB would return 1024. 1.2mb would return 1258291.
If you reorganize the capturing group in your regex like so: /(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i
you can do something like:
function unhumanize(text) {
var powers = {'k': 1, 'm': 2, 'g': 3, 't': 4};
var regex = /(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i;
var res = regex.exec(text);
return res[1] * Math.pow(1024, powers[res[2].toLowerCase()]);
}
unhumanize('1 Kb')
# 1024
unhumanize('1 Mb')
# 1048576
unhumanize('1 Gb')
# 1073741824
unhumanize('1 Tb')
# 1099511627776
You've already got a capturing group for the unit prefix, now all you need is a lookup table:
{ 'k', 1L<<10 },
{ 'M', 1L<<20 },
{ 'G', 1L<<30 },
{ 'T', 1L<<40 },
{ 'P', 1L<<50 },
{ 'E', 1L<<60 }
Demo: http://ideone.com/5O7Vp
Although 1258291 is clearly far too many significant digits to get from 1.2MB.
oops, I gave a C# example. The method is still good though.
One liner solution:
"1.5 MB".replace(/(\d+)+(\.(\d+))?\s?(k|m|g|t)?b?/i, function(value, p1, p2, p3, p4) { return parseFloat(p1 + (p2 || ""))*({ 'K' : 1<<10, 'M' : 1<<20, 'G' : 1<<30, 'T' : 1<<40 }[p4] || 1); })
# 1572864

Categories

Resources