February JS Challenge 1: Answers!

·

7 min read

Warning!

Please don't read this if you haven't done the challenge yet, or wish to only find out how it was done!


Solution

So let's take a look at our text to decipher:

54686973007761730061007265616c6c7900746f756768006368616c6c656e676500666f72006d65210042757400490064696400697421

What a mess!

There are numbers in there, letters, it's a tough one to decipher!

All the letters though... they don't go above 'f' in the alphabet.

In my head, and what you too should look at if you're trying to figure out what something like this may be, is that this is hexadecimal - but what is hexadecimal?

Hexadecimal?

So, 'hexa' means 6 - a hexagon is a 6-sided shape.

Decimal means a number between 1 and 10.

So hexa-decimal is a number between 1 and 16.

We can't write this normally though, the numbers only go 0-9, then we're back at 0 again - normal numbers are like this:

The number 111 in decimal terms is described like this:

1            1            1
hundreds     tens         units

So how do we put a number 1-16 in one 'spot' as it were?

We use 0-9, and then we use A-F

So the numbers 0-9 represent 0-9, as they usually would.

The letters A-F represent 10-15.
So the counting order would be: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.

I hope I explained that okay to you - if I didn't, please go look at a video on Youtube about hexadecimal numbers, this would help you understand it more.

Why does this matter for our text though?

Let's look at our code again, and, I'm going to split it into 2 character parts for the whole String:

54 68 69 73 00 77 61 73 00 61 00 72 65 61 6c 6c 79 00 74 6f 75 67 68 00 63 68 61 6c 6c 65 6e 67 65 00 66 6f 72 00 6d 65 21 00 42 75 74 00 49 00 64 69 64 00 69 74 21

Doesn't really make it any easier to read does it - well, I'll come back to why I did this, later.

ASCII

ASCII is how letters on your keyboard are represented by the computer. You see an 'a' or an 'F' or even a forward slash '/' or a '%' percent sign. The computer knows you've typed this because the keyboard will pass to the CPU, a code. This is called an ASCII code. If you want to know what character is represented by what number, you can look up an ASCII table - this is an example of a part of one:

The numerical code of the character is then represented by the character next to it.

So what?

Well, once you are used to seeing patterns like this, you can start to formulate a plan as to how to solve it - this, to me, now, looks like a string of ASCII characters, but they're in hexadecimal. So in my mind, we need to take the following steps:

  1. Turn those hexadecimals into numbers

  2. Turn those numbers into their ASCII equivalent

  3. Smoosh them all together into one string and see what it says

Finally, some code!

The first thing I want to do is turn the hexadecimals into numbers. So we know:

  • The text needs to be split up every 2 characters

  • We need to turn the hex into decimal

  • We need to add that number to an array (arrays are best for creating lists in any coding language)

// A variable with the text we want to decipher:
let text = '54686973007761730061007265616c6c7900746f756768006368616c6c656e676500666f72006d65210042757400490064696400697421'

// split the text every 2 characters using a for loop and an array
let decipheredText = []
for (let i = 0; i < text.length; i += 2) {
    decipheredText.push(text.substr(i, 2))
}

So, here we create an array called decipheredText for our array, and then we use a 'for loop' to go through the text.

It'll start at 0 (let i = 0), stop at the length of the text (i < text.length), and jump every 2 characters (i += 2).

It'll use the substr method to take a substring, or a part of the string, in this case, at the point 'i' in the text, which represents the position we are at in the text, which the for loop is helping us cycle through, and taking the 2nd character (text.substr(i,2)).

It'll then push, or add this sub-string to the array.

Now we have an array of hexadecimal numbers, each one 2 characters long. Let's see what this looks like:

[
  '54', '68', '69', '73', '00', '77', '61',
  '73', '00', '61', '00', '72', '65', '61',
  '6c', '6c', '79', '00', '74', '6f', '75',
  '67', '68', '00', '63', '68', '61', '6c',
  '6c', '65', '6e', '67', '65', '00', '66',
  '6f', '72', '00', '6d', '65', '21', '00',
  '42', '75', '74', '00', '49', '00', '64',
  '69', '64', '00', '69', '74', '21'
]

What I'd like to do next is to create another array, with each of these represented as decimal numbers:

// go through the decipheredText array and convert each hex value to a decimal number
for (let i = 0; i < decipheredText.length; i++) {
    decipheredText[i] = parseInt(decipheredText[i], 16)
}

So this time, I'm replacing the element 'i' of the decipheredText array with a parseInt or a parsed integer - what I'm doing here, is changing that hexadecimal number into a decimal number. The parseInt method knows this because I specified the number '16' as an argument.

Once this line of code is finished, it has replaced the array with a list of decimal numbers, like so:

[
   84, 104, 105, 115,   0, 119,  97, 115,   0,  97,   0,
  114, 101,  97, 108, 108, 121,   0, 116, 111, 117, 103,
  104,   0,  99, 104,  97, 108, 108, 101, 110, 103, 101,
    0, 102, 111, 114,   0, 109, 101,  33,   0,  66, 117,
  116,   0,  73,   0, 100, 105, 100,   0, 105, 116,  33
]

Okay! Final step now, we need to turn those into ASCII and smoosh them together into one string. Let's do that in one hit, I think we can do it:

// go through the decipheredText array and convert each decimal number to a character and add them to a string
let finalText = ''
for (let i = 0; i < decipheredText.length; i++) {
    finalText += String.fromCharCode(decipheredText[i])
}

We create a finalText string to hold the final string, or our answer.

We have a for loop that goes through the array and uses the String.fromCharCode method to turn that decimal number into its ASCII character. A very good method to know.

What does our string look like? Let's run it and see:

Thiswasareallytoughchallengeforme!ButIdidit!

Oh. I guess if I look at it hard, it's a sentence. It doesn't look nice though does it?

Hmm. Let's look at our original text again, split up into 2 characters:

54 68 69 73 00 77 61 73 00 61 00 72 65 61 6c 6c 79 00 74 6f 75 67 68 00 63 68 61 6c 6c 65 6e 67 65 00 66 6f 72 00 6d 65 21 00 42 75 74 00 49 00 64 69 64 00 69 74 21

Notice anything in there that repeats occasionally?

If you do, great! If not, don't worry - the repeating thing is '00'

I used a '00' in my text to represent a space to split up the characters - let's modify our code so that:

  • If a '00' is found, convert that to a 'space' character.

  • Otherwise, turn it into its ASCII equivalent as normal

let finalText = ''
for (let i = 0; i < decipheredText.length; i++) {
    // if we see a 0, turn it into a space
    if (decipheredText[i] === 0) {
        finalText += ' '
    // otherwise turn it into an ASCII character
    } else {
        finalText += String.fromCharCode(decipheredText[i])
    }
}

So, after all that, what have we got now?

This was a really tough challenge for me! But I did it!

We did do it!

Summary

We learnt a few things today, and if you didn't learn, you practised which will help reinforce that learning.

  • Hexadecimal Numbers

  • Parsing Integers

  • Converting those Decimals into ASCII characters

  • Identifying patterns in text you see

I hope you all enjoyed this challenge, regardless of whether you participated or not and I hope you enjoy this format of blogging!

I'd love to hear your feedback in the comments ❤️