Alphabet code help (Strings)

making a game where in some part you need to solve a code really fast, and to solve the code its really easy just align the place of the alphabets place i the alphabet for example A being 1, B being 2, with the only exception being Z = 0. Anyways since the number systems each digit only goes from 0-9, im left with like more thanhalf of the other letters left out. :frowning:

local function EncryptCode(Code)
	local ReturnCode = ""
	for Index, Letter in ipairs(string.split(Alphabet, ",")) do
		CodeKey[Letter] = Index
	end
	CodeKey.z = 0
	
	for _, Digit in ipairs(string.split(Code, "")) do
		for key, value in pairs(CodeKey) do
			if tostring(value) == Digit then
		 		ReturnCode = ReturnCode..key
			end
		end
	end		

	print(ReturnCode) - prints abhdci
end
EncryptCode(tostring(128439)) -- just an example code

to solve this i wish to randomly choose 2 digits once, twice, or even thrice. And simply take the two digits and combine them and get there value like this. very simple.

combinedDigit = tonumber(FirstDigit..SecondDigit) -- some combined digit

for _, Digit in ipairs(string.split(Code, "")) do
		for key, value in pairs(CodeKey) do
			if tostring(value) == combinedDigit then
		 		return value
			end
		end
	end		

but how would I go about on choosing digits to combine without it overlapping and also maybe mix normal single digit value assigned letters?

So you want a function that inputs numbers and outputs letters according to the letter’s position in the alphabet. Here’s a solution I devised:

local alphabet = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"}

local function Encrypt(code:string)
	local str = {}
	for i, num in code do
		local letter = alphabet[num]
		table.insert(str, letter)
	end
	
	return table.concat(str, "")
end

print(Encrypt({1, 10, 11, 9, 5})) -- Output: "AJKIE"

It involves having the input numbers in a table and indexing those numbers with the position of the corresponding letter and adding it to a new table. The function returns the concatenation of the new table, turning it into one string of letters. This also works with double digit inputs like 10 and 11 as shown in the code.

Hope this helps.

2 Likes

this basically does what the original script does but i suppose in a more clean way. the thing is im inputting a six digit number, and thats what it has to encrypt. and i dont only want letters A - H and Z. I want to include the whole alphabet when encrypting. I can read some parts of the code in two digits and therefore include two digit holding numbers. but thats the problem. i dont know how…

You’re trying to map 26 values onto 10 unique keys. That simply isn’t possible. You’ll either have to increase the number of unique keys (potentially symbols?) or reduce the number of values.

1 Like

Could you just use two digits for every letter?
00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25

I wrote a module script for this. The module has conversion functions for converting the number code to different data types so you can use the module regardless of whether the number code is originally defined as a single number, a string or an array containing the number code of each letter.

You can safely use it even with player-inputted number codes because the functions return not only the result but also a success boolean. If the success boolean is false, the code was invalid (and thus the returned result shouldn’t be used).

The module:
--!strict
local AlphabetNumberCodeModule = {}

local alphabetString: string = "zabcdefghijklmnopqrstuvwxy"

local alphabetArray: {string} = table.create(26)
for i: number = 1, 26 do
	alphabetArray[i] = string.sub(alphabetString, i, i)
end

--== validation functions
function AlphabetNumberCodeModule.validateNumberCodeOfLetter(numberCodeOfLetter: number): boolean
	if numberCodeOfLetter < 0 or numberCodeOfLetter > 25 or math.floor(numberCodeOfLetter) ~= numberCodeOfLetter then
		return false
	end
	return true
end

function AlphabetNumberCodeModule.validateNumberCodesOfLetters(numberCodesOfLetters: {number}, numberOfLetters: number): boolean
	if #numberCodesOfLetters ~= numberOfLetters then
		--error(`Incorrect number of number codes of letters. Number of number codes: {#numberCodesOfLetters}; number of letters: {numberOfLetters}.`)
		return false
	end
	for _, numberCodeOfLetter: number in numberCodesOfLetters do
		local isNumberCodeOfLetterValid: boolean = AlphabetNumberCodeModule.validateNumberCodeOfLetter(numberCodeOfLetter)
		if not isNumberCodeOfLetterValid then
			return false
		end
	end
	return true
end

function AlphabetNumberCodeModule.validateNumberCodeDefinedAsSingleNumber(numberCodeAsSingleNumber: number, numberOfLetters: number): boolean
	if numberCodeAsSingleNumber < 0 or numberCodeAsSingleNumber >= 10^(2 * numberOfLetters) or numberCodeAsSingleNumber ~= math.floor(numberCodeAsSingleNumber) then
		return false
	end
	for i: number = numberOfLetters, 1, -1 do
		-- Remainder when dividing by 100.
		-- This gives a number with the last two digits of numberCode
		-- without having to do string operations
		local numberCodeOfLetter: number = numberCodeAsSingleNumber % 100

		local isNumberCodeOfLetterValid: boolean = AlphabetNumberCodeModule.validateNumberCodeOfLetter(numberCodeOfLetter)
		if not isNumberCodeOfLetterValid then
			return false
		end

		-- floor division by 100 (so for example if numberCode was 1125, it would be 11 after this operation)
		-- This cuts of the last two digits (that were used to get the letter of this iteration) of the number
		-- without having to do string operations
		numberCodeAsSingleNumber //= 100
	end
	return true
end

function AlphabetNumberCodeModule.validateNumberCodeDefinedAsString(numberCodeAsString: string, numberOfLetters: number): boolean
	if #numberCodeAsString ~= 2 * numberOfLetters then
		--error("invalid code length")
		return false
	end
	if string.match(numberCodeAsString, "%d+") ~= numberCodeAsString then
		--error("The code contains something else than just digit characters.")
		return false
	end
	local numberCodeAsSingleNumber: number = tonumber(numberCodeAsString) :: number
	local isNumberCodeAsSingleNumberValid: boolean = AlphabetNumberCodeModule.validateNumberCodeDefinedAsSingleNumber(numberCodeAsSingleNumber, numberOfLetters)
	if not isNumberCodeAsSingleNumberValid then
		return false
	end
	return true
end

--== A function for splitting a number code defined as a single number to an array containing number codes of letters
function AlphabetNumberCodeModule.splitNumberCodeIntoNumberCodesOfLetters(numberCode: number, numberOfLetters: number): (boolean, {number})
	local isNumberCodeValid: boolean = AlphabetNumberCodeModule.validateNumberCodeDefinedAsSingleNumber(numberCode, numberOfLetters)
	if not isNumberCodeValid then
		return false, {}
	end
	local numberCodesOfLetters: {number} = table.create(numberOfLetters)
	for i: number = numberOfLetters, 1, -1 do
		-- There's explanation for this math in validateNumberCodeDefinedAsSingleNumber.
		local numberCodeOfLetter: number = numberCode % 100
		numberCodesOfLetters[i] = numberCodeOfLetter
		numberCode //= 100
	end
	return true, numberCodesOfLetters
end

--== Functions for converting number code to different data types
-- This function adds a zeros to the start if necessary so that the length of the string is 2 * numberOfLetters.
function AlphabetNumberCodeModule.convertNumberCodeDefinedAsSingleNumberToString(numberCodeAsSingleNumber: number, numberOfLetters: number): (boolean, string)
	local isNumberCodeAsSingleNumberValid: boolean = AlphabetNumberCodeModule.validateNumberCodeDefinedAsSingleNumber(numberCodeAsSingleNumber, numberOfLetters)
	if not isNumberCodeAsSingleNumberValid then
		return false, ""
	end
	return true, string.format(`%0{2 * numberOfLetters}i`, numberCodeAsSingleNumber)
end

-- This function is pretty much tonumber with custom validation
function AlphabetNumberCodeModule.convertNumberCodeDefinedAsStringToSingleNumber(numberCodeAsString: string, numberOfLetters: number): (boolean, number)
	local isNumberCodeAsStringValid: boolean = AlphabetNumberCodeModule.validateNumberCodeDefinedAsString(numberCodeAsString, numberOfLetters)
	if not isNumberCodeAsStringValid then
		return false, -1
	end
	local numberCodeAsSingleNumber: number = tonumber(numberCodeAsString) :: number
	return true, numberCodeAsSingleNumber
end

function AlphabetNumberCodeModule.convertNumberCodeDefinedAsArrayOfNumberCodesOfLettersToSingleNumber(numberCodesOfLetters: {number}, numberOfLetters: number): (boolean, number)
	local isArrayOfNumberCodesOfLettersValid: boolean = AlphabetNumberCodeModule.validateNumberCodesOfLetters(numberCodesOfLetters, numberOfLetters)
	if not isArrayOfNumberCodesOfLettersValid then
		return false, -1
	end
	local number: number = 0
	for i: number, numberCodeOfLetter: number in numberCodesOfLetters do
		number += numberCodeOfLetter * 10^(2 * (numberOfLetters - i))
	end
	return true, number
end

--== Functions for Converting number code to letter code.
function AlphabetNumberCodeModule.convertNumberCodesOfLettersToLetterCode(numberCodesOfLetters: {number}, numberOfLetters: number): (boolean, string)
	local isArrayOfNumberCodesOfLettersValid: boolean = AlphabetNumberCodeModule.validateNumberCodesOfLetters(numberCodesOfLetters, numberOfLetters)
	if not isArrayOfNumberCodesOfLettersValid then
		return false, ""
	end
	local letters: {string} = table.create(numberOfLetters)
	for i: number, numberCodeOfLetter: number in numberCodesOfLetters do
		local letter: string = alphabetArray[numberCodeOfLetter + 1]
		letters[i] = letter
	end
	local letterCode: string = table.concat(letters)
	return true, letterCode
end

function AlphabetNumberCodeModule.convertNumberCodeDefinedAsSingleNumberToLetterCode(numberCode: number, numberOfLetters: number): (boolean, string)
	local splitSuccess: boolean, numberCodesOfLetters: {number} = AlphabetNumberCodeModule.splitNumberCodeIntoNumberCodesOfLetters(numberCode, numberOfLetters)
	if not splitSuccess then
		return false, ""
	end
	return AlphabetNumberCodeModule.convertNumberCodesOfLettersToLetterCode(numberCodesOfLetters, numberOfLetters) 
end

function AlphabetNumberCodeModule.convertNumberCodeDefinedAsStringToLetterCode(numberCodeAsString: string, numberOfLetters: number): (boolean, string)
	local conversionSuccess: boolean, numberCodeAsSingleNumber: number = AlphabetNumberCodeModule.convertNumberCodeDefinedAsStringToSingleNumber(numberCodeAsString, numberOfLetters)
	if not conversionSuccess then
		return false, ""
	end
	return AlphabetNumberCodeModule.convertNumberCodeDefinedAsSingleNumberToLetterCode(numberCodeAsSingleNumber, numberOfLetters)
end

return AlphabetNumberCodeModule
A test script
--!strict
local AlphabetNumberCodeModule = require(script.Parent.AlphabetNumberCodeModule)

local numberOfLetters: number = 3

-- You could write these without the leading zeros.
-- The leading zeros won't affect the bytecode.
local exampleNumberCodesAsSingleNumbers: {number} = {
	000010,
	101100,
	040326 -- invalid (26 > 25)
}

local exampleNumberCodesAsStrings: {string} = {
	"01a213", -- invalid (unallowed character "a")
	"", -- invalid (invalid length)
	"031221"
}

local exampleNumberCodesAsArraysOfNumberCodesOfLetters: {{number}} = {
	{00, 08, 11},
	{65, 10, 13}, -- invalid (65 > 25)
	{12, 00, 00}
}

print("--== Examples defined as single numbers:")
for _, exampleNumberCodeAsSingleNumber: number in exampleNumberCodesAsSingleNumbers do
	local successOfConvertingToString: boolean, exampleNumberCodeAsString: string = AlphabetNumberCodeModule.convertNumberCodeDefinedAsSingleNumberToString(exampleNumberCodeAsSingleNumber, numberOfLetters)
	if not successOfConvertingToString then
		print(`{exampleNumberCodeAsSingleNumber}: ERROR.`)
		continue
	end
	local _, letterCode: string = AlphabetNumberCodeModule.convertNumberCodeDefinedAsSingleNumberToLetterCode(exampleNumberCodeAsSingleNumber, numberOfLetters)
	print(`{exampleNumberCodeAsString}: {letterCode}`)
end

print("--== Examples defined as strings:")
for _, exampleNumberCodeAsString: string in exampleNumberCodesAsStrings do
	local successOfConversionToLetterCode: boolean, letterCode: string = AlphabetNumberCodeModule.convertNumberCodeDefinedAsStringToLetterCode(exampleNumberCodeAsString, numberOfLetters)
	if not successOfConversionToLetterCode then
		print(`{exampleNumberCodeAsString}: ERROR.`)
		continue
	end
	print(`{exampleNumberCodeAsString}: {letterCode}`)
end

local function convertArrayOfNumberCodesOfLettersToString(numberCodesOfLetters: {number}): string
	local letterNumberCodeStrings: {string} = table.create(#numberCodesOfLetters)
	for i: number, numberCodeOfLetter: number in numberCodesOfLetters do
		letterNumberCodeStrings[i] = string.format("%02i", numberCodeOfLetter)
	end
	local arrayString: string = table.concat(letterNumberCodeStrings, ", ")
	return arrayString
end

print("--== Examples defined as arrays of number codes of letters:")
for _, exampleNumberCodeAsArrayOfNumberCodesOfLetters: {number} in exampleNumberCodesAsArraysOfNumberCodesOfLetters do
	local arrayString: string = convertArrayOfNumberCodesOfLettersToString(exampleNumberCodeAsArrayOfNumberCodesOfLetters)
	local successOfConversionToLetterCode: boolean, letterCode: string = AlphabetNumberCodeModule.convertNumberCodesOfLettersToLetterCode(exampleNumberCodeAsArrayOfNumberCodesOfLetters, numberOfLetters)
	if not successOfConversionToLetterCode then
		print(`{arrayString}: ERROR.`)
		continue
	end
	print(`{arrayString}: {letterCode}`)
end

print("--== Conversion tests")
local numberToStringSuccess: boolean, numberCodeAsString: string = AlphabetNumberCodeModule.convertNumberCodeDefinedAsSingleNumberToString(000123, numberOfLetters)
print(`number to string: {if numberToStringSuccess then numberCodeAsString else "ERROR"}`)
local stringToNumberSuccess: boolean, stringCodeAsNumber: number = AlphabetNumberCodeModule.convertNumberCodeDefinedAsStringToSingleNumber("000123", numberOfLetters)
print(`string to number: {if stringToNumberSuccess then stringCodeAsNumber else "ERROR"}`)
local arrayToNumberSuccess: boolean, arrayCodeAsNumber: number = AlphabetNumberCodeModule.convertNumberCodeDefinedAsArrayOfNumberCodesOfLettersToSingleNumber({23, 00, 14}, numberOfLetters)
print(`array to number: {if arrayToNumberSuccess then arrayCodeAsNumber else "ERROR"}`)
1 Like

for example 123456 could be written as abcdef or lcdef where L takes the place of 12 cuz its the 12th letter

holy. dang bruhthis is basically everything i need but maybe it was my fault if i didnt get something across because i feel like it only solves half my problem. butthe main goal was to randomly have have some parts of the code read in two like 15, 23, 55 and some to be read like 1, 2, 7 ,9. like a code 132455.
we can write it as acbdee, which is the current version. or we can choose some parts to be read in two like 13 or 24, and some can be put as its usual one digit things, what gets chosen and not chosen to be read in two should be completely random.

but even then this is a lot and thanks

Yes, I’m aware my suggestion isn’t exactly what you were asking for. It was just an alternative solution to this problem:

Initially, I didn’t properly understand the randomizing approach you want so I just presented an alternative but I think I’ve now understood what you want. You want there to be exactly one valid number code for any letter code but potentially multiple valid letter codes for a number code, right? I think this code should give you a letter code formed in the way you want.

--!strict
local AlphabetNumberCodeModule = {}

local alphabetString: string = "zabcdefghijklmnopqrstuvwxy"

local alphabetArray: {string} = table.create(26)
for i: number = 1, 26 do
	alphabetArray[i] = string.sub(alphabetString, i, i)
end

local function encrypt(numberCode: number, numberOfDigits: number, chanceOfConvertingWholePairToLetter: number): string
	if numberCode >= 10^numberOfDigits then
		error("invalid numberCode")
	end
	local letters: {string} = {}
	local numberOfDigitsConverted: number = 0
	while numberOfDigitsConverted < numberOfDigits do
		local digitPair: number = numberCode % 100
		local convertWholePairToLetter: boolean = false
		-- The condition digitPair >= 10 is needed so that
		-- the requirement "exactly one valid number code for any letter code"
		-- is satisfied.
		-- digitPair >= 10 ensures that a digitPair that
		-- is converted to a letter is never equal to a single digit.
		-- Otherwise, when converting the letter code back to a number,
		-- there would be no way to know whether
		-- z, a, b, c, d, e, f, g, h or i should be converted to
		-- a (0, digit)-pair like 01 or to a single digit like 1.
		if digitPair >= 10 and digitPair <= 25 then
			convertWholePairToLetter = math.random() < chanceOfConvertingWholePairToLetter
		end
		if convertWholePairToLetter then
			local letter: string = alphabetArray[digitPair + 1]
			-- Inserting at index 1 to reverse the order of the array.
			-- The item that was inserted in the first loop iteration will be the
			-- last item in the array, item inserted on second loop iteration
			-- will be the second last item in the array etc.
			table.insert(letters, 1, letter)
		else
			local secondDigit: number = digitPair % 10
			local secondDigitLetter: string = alphabetArray[secondDigit + 1]
			table.insert(letters, 1, secondDigitLetter)
		end
		numberOfDigitsConverted += if convertWholePairToLetter then 2 else 1
		numberCode //= if convertWholePairToLetter then 100 else 10
	end
	local letterCode: string = table.concat(letters)
	return letterCode
end

--== Testing
local numberOfDigits: number = 5
local chanceOfConvertingWholePairToLetter: number = .5

local numberCode: number = 20132

local letterCode: string = encrypt(numberCode, numberOfDigits, chanceOfConvertingWholePairToLetter)
print(letterCode)

Also, the player that is supposed to solve the code is given the letter code and they need to convert it to a digit code, right? Why don’t you directly choose the letters (the encrypted code)?

1 Like

in my game there is some trap thing and to stop the trap thing in a short time. and i cant just have the same code otherwise itll be no fun.

also thank you a lot youve written atleast 200 lines of code for my problem :sob: I was gonna give up. thanks a lot again!

1 Like

You could easily generate a random sequence of letters by choosing a random integer in the interval [1, 26] and then choosing a letter using that. Is this not an option?

local alphabetString: string = "zabcdefghijklmnopqrstuvwxy"

local alphabetArray: {string} = table.create(26)
for i: number = 1, 26 do
	alphabetArray[i] = string.sub(alphabetString, i, i)
end

local function generateRandomLetterSequence(numberOfLetters: number): string
	local letters: {string} = table.create(numberOfLetters)
	for i: number, = 1, numberOfLetters do
		letters[i] = alphabetArray[math.random(26)]
	end
	return table.concat(letters)
end
1 Like

ive never thought of doing the reverse :thinking: thr main goal is to have a decrypted number sequence in letters that can be solved by A =1 etc, but i suppose that could also be an option.

Is your current approach this?

  1. You define a number code somehow.
  2. You convert that to a partially randomized letter code.
  3. The player enters a number code.
  4. You check whether the number code given by the player is equal to the one you defined.

If it is, couldn’t it be changed to this?

  1. You generate a random letter code.
  2. The player enters a number code
  3. You generate the correct number code from the letter code and check whether it is equal to the one the player inputted

Here’s some code for converting a letter code to a number code and getting the number of digits in the number code.

local AlphabetNumberCodeModule = {}

local alphabetString: string = "zabcdefghijklmnopqrstuvwxy"

local alphabetArray: {string} = table.create(26)
for i: number = 1, 26 do
	alphabetArray[i] = string.sub(alphabetString, i, i)
end

local numbersForAlphabets: {[string]: number} = {}
for i: number, letter: string in alphabetArray do
	numbersForAlphabets[letter] = i - 1
end

local function getNumberOfDigitsAndCorrectNumberCodeFromLetterCode(letterCode: string): (number, number)
	local numberCode: number = 0
	local howManyDigitsHaveBeenAdded: number = 0
	for iLetter: number = #letterCode, 1, -1 do
		local letter: string = string.sub(letterCode, iLetter, iLetter)
		local letterNumber: number = numbersForAlphabets[letter]
		numberCode += letterNumber * 10^howManyDigitsHaveBeenAdded
		
		local howManyDigitsAreAdded: number = if letterNumber < 10 then 1 else 2
		howManyDigitsHaveBeenAdded += howManyDigitsAreAdded
	end
	return howManyDigitsHaveBeenAdded, numberCode
end

--== Testing
local letterCode: string = "zajdsz"
local numberOfDigits: number, numberCode: number = getNumberOfDigitsAndCorrectNumberCodeFromLetterCode(letterCode)
print(`{letterCode}: {string.format(`%0{numberOfDigits}i`, numberCode)}`)

I should also note that the single number approach I’ve been using in these functions will not work if the number of digits is too big. A Luau number can represent integers up to 2^53 exactly and 2^53 is between 10^15 and 10^16 so the number of digits must be at most 15 digits for my single-number code to be reliable. Since a character can be converted to at most 2 digits, the number of characters in the letter code must be at most 7.

However, you can get rid of these limitations by using a string as you originally did or by using an array of numbers. Here are functions similar to the above but with the difference that the first returns an array of letter number codes instead of one big number code, and the second returns an array of digits.

local function getNumberOfDigitsAndCorrectLetterNumberCodesFromLetterCode(letterCode: string): (number, {number})
	local letterNumberCodes: {number} = table.create(#letterCode)
	local numberOfDigits: number = 0
	for iLetter: number = 1, #letterCode do
		local letter: string = string.sub(letterCode, iLetter, iLetter)
		local letterNumberCode: number = numbersForAlphabets[letter]
		letterNumberCodes[iLetter] = letterNumberCode
		numberOfDigits += if letterNumberCode < 10 then 1 else 2
	end
	return numberOfDigits, letterNumberCodes
end

local function getDigitsFromLetterCode(letterCode: string): {number}
	-- This may be longer than #letterCode after it's filled, but its length is guaranteed to be at least #letterCode.
	local digits: {number} = table.create(#letterCode)
	for iLetter: number  = 1, #letterCode do
		local letter: string = string.sub(letterCode, iLetter, iLetter)
		local letterNumberCode: number = numbersForAlphabets[letter]
		if letterNumberCode < 10 then
			table.insert(digits, letterNumberCode)
		else
			table.insert(digits, letterNumberCode // 10)
			table.insert(digits, letterNumberCode % 10)
		end
	end
	return digits
end

And here’s a modified version of the encrypt function I sent earlier. This uses an array of digits instead of a single number so this doesn’t have the aforementioned limitation either. However, you won’t need this if you just choose a random letter code using the function generateRandomLetterSequence in my earlier reply.

local function encryptArrayOfDigits(digits: {number}, chanceOfConvertingWholePairToLetter: number): string
	local letters: {string} = {}
	local totalNumberOfDigits: number = #digits
	local numberOfDigitsConverted: number = 0
	while numberOfDigitsConverted < totalNumberOfDigits do
		local digitPair: number
		if numberOfDigitsConverted == totalNumberOfDigits - 1 then
			digitPair = digits[totalNumberOfDigits]
		else
			digitPair = 10 * digits[numberOfDigitsConverted + 1] + digits[numberOfDigitsConverted + 2]
		end
		local convertWholePairToLetter: boolean = false
		if digitPair >= 10 and digitPair <= 25 then
			convertWholePairToLetter = math.random() < chanceOfConvertingWholePairToLetter
		end
		if convertWholePairToLetter then
			local letter: string = alphabetArray[digitPair + 1]
			table.insert(letters, letter)
		else
			local firstDigit: number = digitPair // 10
			local firstDigitLetter: string = alphabetArray[firstDigit + 1]
			table.insert(letters, firstDigitLetter)
		end
		numberOfDigitsConverted += if convertWholePairToLetter then 2 else 1
	end
	local letterCode: string = table.concat(letters)
	return letterCode
end
2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.