Discuss Scratch

cofoes
Scratcher
44 posts

Encoding/Decoding Cloud Variables

I am making this because I don’t know how to encode/decode cloud data. I tried using the wiki’s method, but that failed. How do I encode/decode?

moved to _WOFDOG_
UnconstructivePoster
New to Scratch
100+ posts

Encoding/Decoding Cloud Variables

The basic principle is that since cloud variables can only store numeric digits, to store a string you need to first convert it into a numeric format. The easiest way to do this is to map every character to a sequence of digits with a fixed length.

In most cases, 2 digits per character will work fine, but if you want to encode more than 100 different characters, you'll need to use 3+ digits instead of 2. It's important to make sure each digit sequence is the same length, since this makes decoding much easier.

For this example, I'll assume you're only encoding lowercase letters and spaces. Let's say you map the letters in the following way:
"a" --> 01
"b" --> 02
"c" --> 03
...
"z" --> 26
" " --> 27 (the space character)
The first part of your encoding script will be to go through the string you want to store letter by letter and build a new encoded string, in which each letter of the original string is replaced with its corresponding 2-digit sequence. The format of this approach would look like:

set [encoded string v] to [] // starting from nothing, you're "building up" the encoded string piece by piece
set [index v] to [1] // this tracks which letter you're currently encoding; since you start at the first letter, this starts at 1
repeat (length of (original string :: #999999)) // goes through the entire original string
set [current letter v] to (letter (index) of (original string :: #999999)) // gets you the current letter you're working on
...
set [encoded letter v] to (... :: #999999) // we'll fill in this part next
set [encoded string v] to (join (encoded string) (encoded letter)) // tacks on the next encoded letter
change [index v] by (1) // move on to the next letter

Now you have map each letter to its digit code. You could do this using 27 if-statements like if letter = _ then set encoded letter to xx, but that's extremely tedious and also hard to extend if you want to add more characters in the future.

Instead, an easier method is to use the fact that the digit codes we chose are consecutive and start at 1. This allows us to store our entire mapping using the placement of characters in a list, using the following code.

delete (all v) of [character list v]
add [a] to [character list v]
add [b] to [character list v]
...
add [z] to [character list v]
add [ ] to [character list v] // contains a space

Alternatively, you could manually type out the list elsewhere and import it, or use the following code to automate the process a little.

set [characters v] to [abcdefghijklmnopqrstuvwxyz ] // note the space character at the very end
set [index v] to [1]
delete (all v) of [character list v]
repeat (length of (characters))
add (letter (index) of (characters)) to [character list v]
change [index v] by [1]

With this code, if we want to add a new character, all we have to do is put the new character at the end of the value of the “characters” variable, and the rest of the script will take care of things for us.

Notice that “a” is the first character in the list, b the second, c the third… all the way down to z as the 26th and space as the 27th. This perfectly matches the way we set up our digit sequences! How convenient.

We now use this fact to encode each character in the following way.

set [encoded letter v] to (item # of (letter) in [character list v] :: list) // see above explanation

Note, however, that if the item number is less than 10, it will only be one digit long, and we want to make sure all our digit codes are exactly 2 digits long so we can avoid ambiguity when we decode. So we add the following code to pad an extra “0” to the beginning of the encoded letter, if the digit code is only one digit long.

if <(length of (encoded letter)) < (2)> then
set [encoded letter v] to (join [0] (encoded letter))

Finally, we set the value of the cloud variable to our encoded string. It might be useful to do this outside the actual encoding custom block, in case you want to use your encoding scheme in a different situation that doesn't involve cloud variables.

set [☁ cloud variable v] to (encoded string)

Putting everything together, we get the following encoding script, which should function properly assuming I wrote everything correctly.

when green flag clicked
initialize your character list using one of the approaches mentioned above :: #999999

// use the following when you want to encode a string to cloud
encode (your string here :: #999999) :: custom
set [☁ cloud variable v] to (encoded string) // sets the cloud variable's value

define encode (string)
assuming you've finished initializing your character list :: #999999
set [encoded string v] to [] // starting from nothing, you're "building up" the encoded string piece by piece
set [index v] to [1] // this tracks which letter you're currently encoding; since you start at the first letter, this starts at 1
repeat (length of (string)) // goes through the entire original string
set [current letter v] to (letter (index) of (string)) // gets you the current letter you're working on
set [encoded letter v] to (item # of (letter) in [character list v] :: list) // maps the letter to its 2-digit sequence
if <(length of (encoded letter)) < (2)> then
set [encoded letter v] to (join [0] (encoded letter)) // adjusts the length of the encoded letter
end
set [encoded string v] to (join (encoded string) (encoded letter)) // tacks on the next encoded letter
change [index v] by (1) // move on to the next letter
end

You could probably remove the need for some of those variables by combining a few blocks, but I think the variables help with readability.

Now, decoding is basically doing the opposite of encoding: replacing each 2-digit code with its corresponding character. In practice, the script for doing this is a little simpler than the one for encoding. In essence, you go through the encoded string, check every adjacent pair of characters, and map each of those to the character it corresponds to, building up the decoded string in a similar way to the encoding process.

Note how important it was that every digit sequence to be the same length. Imagine if we'd mapped “a” to “1” instead of “01”, for example. We'd have no idea whether “11” meant “aa” or “k”! The fixed-length property removes this kind of ambiguity. It also makes our lives a lot easier while we're iterating.

Once again, since we're iterating through a string, we use a similar structure, with a few minor differences that I'll point out.

set [decoded string v] to [] // starting from nothing, you're "building up" the decoded string piece by piece
set [index v] to [0] // this tracks which pair of letters you're currently encoding; I choose to start it at 0 in this case
repeat (length of (encoded string :: #999999)) // goes through the entire encoded string
set [current encoded digit sequence v] to (join (letter ((index) + (1)) of (encoded string :: #999999)) (letter ((index) + (2)) of (encoded string :: #999999))) // this is the main difference, which I'll explain below
set [decoded letter v] to (... :: #999999) // we'll fill in this part next
set [decoded string v] to (join (decoded string) (decoded letter)) // tacks on the next letter
change [index v] by (2) // moves on to the next pair of letters, which is why we increase by 2

// part of the script got cut off, so here's the reporter block
(join (letter ((index) + (1)) of (encoded string :: #999999)) (letter ((index) + (2)) of (encoded string :: #999999)))

The main difference is that we're working with pairs of digits in the encoded string, instead of individual characters. This means that we want to check adjacent “chunks” of 2 digits, i.e. we increase our index by 2 on each iteration, instead of by 1.

And each 2-digit sequence is, well, 2 digits long instead of 1. Since the (letter () of ()), which is our way of accessing the digits in the encoded string, only gives us one digit at a time, we need to use the (join () ()) block to smush our pair of digits together.

I chose to start the index at 0, but you can equivalently start it at 1 if you want. You'll just have to change (index + 1) and (index + 2) to (index) and (index + 1) respectively, to make sure you're still checking the correct pairs starting from the very first digit in the encoded string.

Once you have that set up, decoding each individual digit sequence is very easy. Once again, we use the fact that the digit sequences correspond to the item numbers of their matching letters, meaning we can simply do:

set [decoded letter v] to (item (current encoded digit sequence) of [character list v])

Putting everything together once again, we arrive at the following decoding script.

// use the following when you want to decode the cloud variable; the variable "decoded string" will store the value you want
decode (☁ cloud variable)

define decode(encoded string)
once again, assuming you've initialized your character list already :: #999999
set [decoded string v] to [] // starting from nothing, you're "building up" the decoded string piece by piece
set [index v] to [0] // this tracks which pair of letters you're currently encoding; I choose to start it at 0 in this case
repeat (length of (encoded string :: #999999)) // goes through the entire encoded string
set [current encoded digit sequence v] to (join (letter ((index) + (1)) of (encoded string :: #999999)) (letter ((index) + (2)) of (encoded string :: #999999)))
set [decoded letter v] to (item (current encoded digit sequence) of [character list v]) // maps the digit sequence to its corresponding letter
set [decoded string v] to (join (decoded string) (decoded letter)) // tacks on the next letter
change [index v] by (2) // moves on to the next pair of letters, which is why we increase by 2

Let me know if this works, and if you have any questions. Hope this helps.

Last edited by UnconstructivePoster (July 15, 2023 00:15:40)


Nestlé is an evil company. Don't buy their products!









Bookmarking some posts:
- explanation of cloud variable string encoding
Spentinium
Scratcher
100+ posts

Encoding/Decoding Cloud Variables

UnconstructivePoster wrote:

– wow he made a scratch wiki article in the forums –
Wow uh… that's more in-depth than the wiki article… you should really contribute to the wiki…

do not ask why i am here
NIKI-KOLCHAGOV
Scratcher
100+ posts

Encoding/Decoding Cloud Variables

Spentinium wrote:

UnconstructivePoster wrote:

– wow he made a scratch wiki article in the forums –
Wow uh… that's more in-depth than the wiki article… you should really contribute to the wiki…

Yea





Powered by DjangoBB