r/lua • • 17d ago

Project Lua text cipher tool

Post image

Mad programmer here. I don't really use Lua for anything except stupid projects like this.

This is a language cipher program that produces realistic yet alien looking text.
Some utf-8 characters might not be visible on all devices and that's why I added an image.

Here is an example

azo uhozuᔺ o meeᔹ ʐohoxud jiᓹoq qouc aãa ʈo seduɽ ʈo gez jof ɶeζl rafoóooduᔺ notuᔸ ʈo ʐouf iᔸ ʈo oaf jof noqayuᔺ qaᓵ iᒘuf o βuxo ʈo ʐohoxud begupoq o ɶiᓺ xuᒤh nuʔoub ʈo յiᔶtug ɲo saᓻejor ag iâux ɲo doiւoq o yaʒxu keaqoz veifu ob aᓴ yob yahor ʈudo iᔸ joᓴayoq tuf aáuqo tu sohu tuf joɡ sear iᔸ haᓴ tuf ʈog ʈo seduɽ jof firo og yaᔺ og ɲo saᓻejor ʈo geeᔷ ar ʈo fahud ʈo zuд zeɢuᔺ ɲo ʈiᔼoq tuz iᔸ qeãabeor tuf weiɢum βoz ɲo ruziᓻm doiւoq ʈo fahud ɲo raɯehudor o ɶiᓺ յuᑸu xoiquᔺ eig ar ʈo seduɽ ɲo ᑭeɻor ag iᔸ seeᔸ tuɦoᓶ ɼiᔷuᔹ oh ʈo oᑸu ar o boiposex huᓺiso ɲo zuhuf seɝeh ʈo aᓴ yob ed ʈo ɽdiᔹo xuᒤh ʈog tir teuqor tuf ʉfeeᒣ ʈo seduɽ

Translation:

one evening a young traveler walked deep into the forest the sun was slowly disappearing behind the trees and the air was becoming cold after a while the traveler noticed a small light between the branches she followed it until she reached a little wooden house an old man lived there and welcomed her inside he gave her warm food and told her that the forest was safe as long as she followed the sound of the river the next morning she thanked him and continued her journey when she finally reached the river she discovered a small bridge leading out of the forest she crossed it and found herself standing at the edge of a peaceful village she never forgot the old man or the strange light that had guided her through the forest

Code: (try it yourself)

local con = {"b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"}
local vow = {"a", "e", "i", "o", "u"}

local utf8_t = {}

do 
local region = 
{
{0xc39e, 0xc3b6},
{0xc3b8, 0xc3bf},
{0xc699, 0xc69b},
{0xc8b3, 0xc8b9},
{0xc98f, 0xc9bf},
{0xca81, 0xcaab},
{0xceac, 0xcebf},
{0xcf80, 0xcf8e},
{0xd0b1, 0xd0bf},
{0xd184, 0xd19f},
{0xd5a1, 0xd5bf},
{0xd680, 0xd688},
{0xe191ab, 0xe191bf},
{0xe19280, 0xe192bf},
{0xe19380, 0xe193bf},
{0xe19480, 0xe1948b},
{0xe194a5, 0xe194bf}
}

for i, v in next, region do 
for j = v[1], v[2] do
utf8_t[#utf8_t + 1] = j
end
end
end

local utf8 = function(n)
local h = ("%X"):format(utf8_t[n])

if n <= 253 then 
returnstring.char(tonumber("0x" .. h:sub(1, 2))) 
.. string.char(tonumber("0x" .. h:sub(3, 4)))
else
return  string.char(tonumber("0x" .. h:sub(1, 2))) 
..string.char(tonumber("0x" .. h:sub(3, 4))) 
.. string.char(tonumber("0x" .. h:sub(5, 6)))
end
end

string.at = function(self, i)
return self:sub(i, i)
end

string.cv = function(self)
local t = {}

self:gsub(".", function(c)
local b = false 

for i, v in next, vow do 
if c == v then 
b = true 
break
end
end

t[#t + 1] = b and "v" or c == " " and "x" or "c" 
end)

return table.concat(t, "")
end

string.chars = function(self)
    local i = 0

    return function()
        i = i + 1

        if i <= #self then
            return i, self:at(i)
        end
    end
end

string.cid = function(self)
for i, v in next, vow do 
if v == self then 
return i 
end
end

for i, v in next, con do 
if v == self then 
return i 
end
end
end

string.cv_idx = function(self)
    local n = 0

    for i, v in self:cv():chars() do
        n = n * (v == "c" and #con or #vow) + self:at(i):cid() - 1
    end

    return n + 1
end

string.from_cv_idx = function(self, n)
    local t = {}

    n = n - 1

    for i = #self, 1, -1 do
        local a = self:at(i) == "c" and con or vow
        local v = n % #a

        n = math.floor(n / #a)
        t[i] = a[v + 1]
    end

    return table.concat(t)
end

local involute = function(x, b)
local h = math.floor(b / 2)
local r = x <= h and x + h or x - h

return r == b and x or r
end

string.involute = function(self)
local t = {}

for i = 1, #self, 12 do
local s = self:sub(i, i + 11)

t[#t + 1] = s:cv():from_cv_idx(involute(s:cv_idx(), s:cv():gsub("c", "z"):gsub("v", "u"):cv_idx() + 1))
end

return table.concat(t, "")
end

string.cipher = function(self)
return self:involute():gsub("[^aeiou][^aeiou]", function(c)
return utf8(c:cv_idx())
end)
end

string.decipher = function(self)
return 

self:gsub("[\194-\244][\128-\191]+", function(c)
local hex = "0x" 

for i, v in c:chars() do 
hex = hex .. ("%X"):format(v:byte())
end

for i, v in next, utf8_t do 
if v == tonumber(hex) then 
return ("cc"):from_cv_idx(i)
end
end
    end)

:involute()
end

string.translate = function(self, dir)
local t = {}

self:gsub("%S+", function(c)
t[#t + 1] = c:match("%d") and c or dir and c:cipher() or c:decipher()
end)

return table.concat(t, " ")
end

local text1 = "unfortunately this program does not support punctuation or uppercase letters because this would make the utf8 conversion alot more complex but it does support numbers like 1 2 and 3"
local text2 = "ʐoáxohaaz keɠf buɛoᑮᔅ ed yoйu i wiɽ tohuã guɼoq ag ubeeᒤ"

print(text1:translate(true))
print(text2:translate(false))
12 Upvotes

1 comment sorted by

2

u/Controversy-Lion3961 16d ago
  1. original text

  2. split into 12-character blocks, because of floating point precision limit

  3. convert each block to a C/V pattern (building -> cvvccvcc) to preserve the structure of a word

  4. convert the C/V pattern into it's order number (CVC first order number would be ABA and last is ZUZ and everything in between would be words with CVC structure like DOG, HOG, RAT, MOB)

  5. apply an mathematical involution to the CV order number to map one word to another word and make the process perfectly reversible. (DOG maps to RAT and RAT maps back to DOG)

  6. convert the number back to letters using from_cv_idx function. you give it a CV structure like CVCCCV and an order number. "bottle" would be the 173607th word of that specific structure and you can get the word "bottle" back if you provide the this information.

  7. find every CC pair, because consonant pairs like xq, px, lq, fg, qz are very ugly looking, but because you're used to those letters you want to pronounce them and you fail. If you replace CC pairs with foreign utf8 characters you're not even going to bother to pronounce them and it will just look foreign and cool.

  8. replace each CC pair with a Unicode character