Decipher Neurotic Frogs
Now that Puzzling.SE has finally cracked my amphibian-obsessed cipher, let's write a program or function to decrypt it!
(If you want to look at the puzzle before having it spoiled for you, click the above link now.)
How the cipher works
In Neurotic Frogs Ought To Relax In Mud Baths ("Neurotic Frogs" for short), every letter is encrypted as one or two words:
- The length of a non-italicized word represents a letter.
neurotic=> 8 letters =>Hfrogs=> 5 letters =>Eperpendicular=> 13 letters =M
- A word that contains italics modifies the following word, adding 10 if the italicized word was odd in length or 20 if the italicized word was even in length. Any or all of the word may be italicized. An italicized word is always followed by a non-italicized word.
*o*ught to=> odd, 2 => 12 =>Llo*u*nging calms=> even, 5 => 25 =>Y
Every word of plaintext corresponds to a sentence of ciphertext, and every sentence of plaintext corresponds to a paragraph of ciphertext.
Input format
Your program or function shall input a message in Neurotic Frogs, formatted in Markdown. The input will consist only of printable ASCII and newlines.
- Words are runs of characters that match the regex
[A-Za-z0-9'].- Numbers and letters both count toward the length of a word.
QB64representsD. - NOTE: Apostrophes do not count toward the length of a word.
Isn'trepresentsD, notE.
- Numbers and letters both count toward the length of a word.
- Italicized letters are wrapped in a pair of asterisks (
*letters*).- One or more consecutive letters may be italicized, up to an entire word (
masseus*es*,*all*); multiple non-consecutive letters in a word may also be italicized (g*e*n*e*rates). - Italics never span multiple words, never include punctuation, and never include apostrophes.
- Unpaired asterisks and multiple adjacent asterisks will never occur.
- One or more consecutive letters may be italicized, up to an entire word (
- Punctuation is any of the following characters:
.,?!:;-()".- Words within a sentence are separated by one or more punctuation characters and/or a single space. Examples:
*all* welcomed,toad*s*, newts,Ever*y*one--frogs,cap... bliss,they're (I - Sentences end with one or more punctuation characters and are separated by a double space:
Th*e* Montgomery A*m*phibian Salon! Come luxuriate today! - Paragraphs are separated by a single newline. (The last sentence of a paragraph still has one or more punctuation characters at the end.)
- Words within a sentence are separated by one or more punctuation characters and/or a single space. Examples:
Other characters will not appear in input and do not need to be handled.
Your code may, at your discretion, expect input to have a single trailing newline.
Output format
The result of decrypting the input will be one or more sentences. Letters of plaintext may be any combination of upper- and lowercase. Words within a sentence must be separated by single spaces. Sentences must end with a period (.) and be separated by a single space. You may output a trailing space after the last sentence. Your output will all be on one line, but you may output a trailing newline.
Miscellaneous details
Your code may use any of the standard input and output methods. It must receive input as a multiline string, not a list or other data structure, and it must output a string.
The shortest code in bytes wins!
Test cases
-->
Neurotic Frogs *O*ught To Rel*a*x In *M*ud Baths!
<--
HELLO.
-->
Business standards all*o*w only *adult* amphibians.
<--
HINT.
-->
Rejoice, *a*ll frogs an*d* toads also! Montgomery Sal*o*n opens up! Ha*pp*y throng fill*s* street ecstatically!
<--
GOOD JOB PPL.
-->
I like 3.1415926535897.
IM*O*, it's a *b*la*st*, yeah!
<--
ADAM. MAN.
-->
*I*, happily, *th*anks 2 u *e*ditin*g* specific wor*ding*--clarifying a *bit*--betterment :D!
<--
QUARTATA.
-->
Perpendicular l*ou*nging calms. *A* frog, a m*u*d cap... bliss! Wallowing g*e*n*e*rates happiness. Amphibian sp*a* isn't expensive--seventy d*o*llars--cheap! That'*s* not *a* large e*x*pens*e* from an*y* discerning fr*o*g's money, unlik*e* Super 8.
Ever*y*one--frogs, toad*s*, newts, *a*nd salamanders! G*e*t a wonderful shiat*s*u, or recei*v*e an other kind. Masseus*es* are her*e* today! Invite a fianc*e*e, supervisor, roommate, niece: *all* welcomed!
Y*o*u simply ne*v*er believed these p*o*ssibilitie*s*; they're (I *swear*) absolute truth! Th*e* Montgomery A*m*phibian Salon! Come luxuriate today!
<--
MY NAME IS INIGO MONTOYA. YOU KILLED MY FATHER. PREPARE TO DIE.
-
4\$\begingroup\$ +1 for the princess bride input. Oh, and for your skill, that too. \$\endgroup\$Magic Octopus Urn– Magic Octopus Urn2016年09月11日 00:34:56 +00:00Commented Sep 11, 2016 at 0:34
-
\$\begingroup\$ Is a word containing italics guaranteed to be followed by one not containing italics? \$\endgroup\$R. Kap– R. Kap2016年09月11日 09:31:52 +00:00Commented Sep 11, 2016 at 9:31
-
\$\begingroup\$ @R.Kap Correct. I've edited the question to clarify that. \$\endgroup\$DLosc– DLosc2016年09月12日 03:12:06 +00:00Commented Sep 12, 2016 at 3:12
7 Answers 7
Perl, 72 bytes
#!perl -n
$x=/\*/?2-y/'//c%2:!print/ /?$':chr$x.0+y/'//c+64for/[\w*']+| /g,' . '
Counting the shebang as one, input is taken from stdin.
Sample Usage
$ more in.dat
Neurotic Frogs *O*ught To Rel*a*x In *M*ud Baths!
Perpendicular l*ou*nging calms. *A* frog, a m*u*d cap... bliss! Wallowing g*e*n*e*rates happiness. Amphibian sp*a* isn't expensive--seventy d*o*llars--cheap! That'*s* not *a* large e*x*pens*e* from an*y* discerning fr*o*g's money, unlik*e* Super 8.
Ever*y*one--frogs, toad*s*, newts, *a*nd salamanders! G*e*t a wonderful shiat*s*u, or recei*v*e an other kind. Masseus*es* are her*e* today! Invite a fianc*e*e, supervisor, roommate, niece: *all* welcomed!
Y*o*u simply ne*v*er believed these p*o*ssibilitie*s*; they're (I *swear*) absolute truth! Th*e* Montgomery A*m*phibian Salon! Come luxuriate today!
$ perl neurotic-frogs.pl < in.dat
HELLO. MY NAME IS INIGO MONTOYA. YOU KILLED MY FATHER. PREPARE TO DIE.
-
1\$\begingroup\$ I'm awarding the bounty to this answer, since it's the shortest one at the end of the bounty period besides my own (indeed, the only one that came anywhere close). \$\endgroup\$DLosc– DLosc2016年09月26日 19:39:18 +00:00Commented Sep 26, 2016 at 19:39
JavaScript (ES6), (削除) 172 (削除ここまで) (削除) 169 (削除ここまで) (削除) 157 (削除ここまで) 150 bytes
Saved 10 bytes thanks to @Neil
x=>x.match(/[\w'*]+|\s+/g).map(y=>y[0]==" "?y[1]:y==`
`?". ":/\*/.test(y,l+=y.match(/\w/g).length)?(l=l%2*10+19,""):l.toString(36,l=9),l=9).join``+"."
Can probably be further improved. Outputs in all lowercase.
-
\$\begingroup\$ Save 2 bytes by moving the
i=0into thetoString. \$\endgroup\$Neil– Neil2016年09月11日 09:46:48 +00:00Commented Sep 11, 2016 at 9:46 -
\$\begingroup\$ Out of interest, I had a go at fixing those bugs and came up with this:
x=>x.replace(/([\w*']+)[^\w\n*' ]* ?( ?)/g,(_,y,z)=>/\*/.test(y,l=y.replace(/'/g ,"").length)?(i=l%2||2,""):l+i*10+9).toString(36,i=0)+z,i=0).replace(/\n|$/g,". ")\$\endgroup\$Neil– Neil2016年09月11日 09:48:19 +00:00Commented Sep 11, 2016 at 9:48 -
\$\begingroup\$ Seems to work in it's current form. \$\endgroup\$primo– primo2016年09月11日 12:36:51 +00:00Commented Sep 11, 2016 at 12:36
-
\$\begingroup\$ @Neil Thanks. That saves 12 bytes, but it doesn't work on the last test case. Fixing that adds 9 for a net shortening of 3 bytes. \$\endgroup\$ETHproductions– ETHproductions2016年09月11日 13:59:58 +00:00Commented Sep 11, 2016 at 13:59
-
\$\begingroup\$ @Neil Getting rid of
.replaceand just using.matchsaved another 12 bytes. \$\endgroup\$ETHproductions– ETHproductions2016年09月11日 14:17:08 +00:00Commented Sep 11, 2016 at 14:17
Pip, (削除) 65 (削除ここまで) 64 bytes
The score is 62 bytes of code + 2 for the -rs flags.
Flg{O{{(zy*t+#a-1)X!Y'*Na&2-#a%2}MJa@`[\w*]+`}MlRM''^sX2O". "}
Explanation
The -r flag reads all lines of stdin and stores a list of them in g. The -s flag sets the output format of lists to space-separated.
The easiest way to read this code is from the outside in:
Flg{...} For each line l in g, do:
O{...}MlRM''^sX2O". " Translate a paragraph into a sentence of plaintext:
lRM'' Remove apostrophe characters
^sX2 Split on " " into sentences
{...}M Map the below function to each sentence
O Output the result list, space-separated, without newline
O". " Output that string, without newline
{...}MJa@`[\w*]+` Translate a sentence into a word of plaintext:
a@`[\w*]+` Find all matches of regex (runs of alphanumeric and *)
{...}MJ Map the below function to each word and join into string
(zy*t+#a-1)X!Y'*Na&2-#a%2 Translate a word into a letter of plaintext:
#a-1 Length of word minus 1
y*t+ Add 10 or 20 if y is set (see below)
(z ) Use that number to index into lowercase alphabet
'*Na& Count * characters in word, logical AND with...
2-#a%2 2 if word is even length, 1 if odd
Y Yank that value into y, to modify the following word
X! String multiply the character by not(y)
If y is truthy, the word had italics, and we get ""
If y is falsy, the word had no italics, and we get a letter
-
\$\begingroup\$ Seems unbeatable. \$\endgroup\$primo– primo2016年09月21日 14:21:21 +00:00Commented Sep 21, 2016 at 14:21
Python 2, (削除) 238 (削除ここまで) (削除) 221 (削除ここまで) (削除) 218 (削除ここまで) (削除) 214 (削除ここまで) (削除) 207 (削除ここまで) 205 bytes
from re import*
def f(x):
d='';m=0
for w in split(r"[^\w\d*'~\n]+",sub(' ','~',x))[:-1]:l=len(sub("[*'~\n]",'',w));q='*'in w;d+='. '[w[0]>'}':]*(w[0]in'~\n')+chr(64+l+m)[q:];m=(2-l%2)*10*q
print d+'.'
Uses lots of regex to do the processing. We transform the double-space into ~ and use that to process it. ~ and \n are handled specially.
The biggest character gain comes from the preprocessing of the input in the for line; this can definitely be golfed further.
Ideone it! (all test cases)
Saved 7 bytes thanks to DLosc!
Python 2.7, (削除) 390 (削除ここまで) (削除) 342 (削除ここまで) (削除) 341 (削除ここまで) (削除) 339 (削除ここまで) 335 bytes:
from re import*
def F(i):
W=X="";S,s=split,sub;D='[^\w\s*]';Q=lambda c,t:len(s(D,X,c.group()).split()[t])
for m in S('\W\n',s(D+"*\w*\*\w+\*.*?(?=\s) \w+",lambda v:"a"*([20,10][Q(v,0)%2]+Q(v,1)),s("'",X,s("--"," ",i)))):
for g in S('\W ',m):
for q in S('\W',g):
W+=chr(64+len(q))
W+=" "
W=W[:-1]+". "
print s("@",X,W)
Takes input in the format:
F('''Multi or Single-lined String''')
Can be golfed down a lot more, which I will do whenever I get the chance.
Explanation:
Uses the immense power of Python's regular expression built-ins to decipher the input. This is the fundamental process the function goes through for each input:
Firstly, all
--are replaced with a single space, and every apostrophe is removed. Then, all words containing italicized components and the word proceeding it are both matched in one string and replaced with10 + len(second word)number of consecutiveas if the length of the first word isodd, and20 + len(second word)consecutiveas otherwise. This makes use of the following regular expression:[^\w\s*]*\w*\*\w+\*.*?(?=\s) \w+For example, if we have the sentence
Perpendicular l*ou*nging calms.,l*ou*nging calmswill be replaced withaaaaaaaaaaaaaaaaaaaaaaaaa, or 25as, sincel*ou*nginghas an even number of characters andcalmshas 5.20+5=25.Now, the newly modified input is split at each punctuation mark followed by a newline (
\n) to get the paragraphs, then each paragraph is split at each punctuation followed by 2 spaces to get the sentences, and finally, each sentence is split into words along any punctuation including a space. Then, for each word (including the runs of consecutiveas), we add on to a stringWthe letter corresponding to the unicode code point64(the unicode code point of the character beforeA, which is@) pluslen(word). We then add a single space toWonce all the words of a sentence have been exhausted, and when all the sentences in a paragraph are exhausted, we add a.followed by a single space.Finally, after the entire input has been gone through,
Wis output tostdoutas the deciphered message.
-
\$\begingroup\$ Minor nitpick: spec says output sentences are separated by single space, not double (this change also saves a byte). Initial golfing suggestion: since you're importing everything from
re, usesubinstead ofstr.replace. More general golfing suggestion: it's probably more efficient to treat everything that's not a word or*as punctuation. Saves on big huge character classes. \$\endgroup\$DLosc– DLosc2016年09月12日 03:41:32 +00:00Commented Sep 12, 2016 at 3:41 -
\$\begingroup\$ @DLosc Oh, my bad. I thought the spec was to separate sentences in the output by 2 spaces. I'll fix that. Also, thanks for the golfing suggestions! I'll see what I can do with those. \$\endgroup\$R. Kap– R. Kap2016年09月12日 04:24:53 +00:00Commented Sep 12, 2016 at 4:24
PHP, 196 Bytes
<?preg_match_all("#[\w*']+| |$#m",$_GET[s],$m);foreach($m[0]as$s){if(!$s||$s==" ")echo!$s?". ":" ";else{$l=$p+64+strlen(str_replace("'","",$s));if(!$p=strstr($s,"*")?20-$l%2*10:0)echo chr($l);}}
If I could assume that there is only one Apostrophe in the middle of a word 194 Bytes
<?preg_match_all("#[\w*]+(<?=')[\w*]+|[\w*]+| |$#m",$_GET[s],$m);foreach($m[0]as$s){if(!$s||$s==" ")echo!$s?". ":" ";else{$l=$p+64+strlen($s);if(!$p=strstr($s,"*")?20-$l%2*10:0)echo chr($l);}}
-
\$\begingroup\$ @DLosc It was urlencoded
%0Aas a functionrawurlencode("\n"). I prefer in this case a form with a textarea for the input and so my html site makes it automatically to encode the string \$\endgroup\$Jörg Hülsermann– Jörg Hülsermann2016年09月21日 17:59:21 +00:00Commented Sep 21, 2016 at 17:59 -
\$\begingroup\$ @DLosc I suspect that error_reporting in the php.ini is on. try 'error_reporting(0);' after the
<?. One error belongs to$_GET[s]it works but correct is$_GET["s"]and it's better to declareand initialize the variable$p=0;before the loop. Now my question at you is: Can I ssume that in one Word is only one Apostrophe in the middle of the Word? \$\endgroup\$Jörg Hülsermann– Jörg Hülsermann2016年09月21日 18:41:21 +00:00Commented Sep 21, 2016 at 18:41 -
\$\begingroup\$ @DLosc for multiple Apostrophes I must use my first answer. The second - 2 Bytes works only with one Apostroph in the middle if the word. \$\endgroup\$Jörg Hülsermann– Jörg Hülsermann2016年09月21日 20:13:27 +00:00Commented Sep 21, 2016 at 20:13
-
\$\begingroup\$ I figured out what my problem was--my server doesn't have short opening tags enabled. Changing to
<?phpworked. \$\endgroup\$DLosc– DLosc2016年09月21日 20:19:57 +00:00Commented Sep 21, 2016 at 20:19 -
\$\begingroup\$ @Dlosc I have never use
<?in reality. I use the short tag only in my post here. Now I know that it can resut in a blank page. \$\endgroup\$Jörg Hülsermann– Jörg Hülsermann2016年09月21日 20:49:39 +00:00Commented Sep 21, 2016 at 20:49
PHP, (削除) 231 (削除ここまで) (削除) 226 (削除ここまで) 228 bytes
for a start
<?preg_match_all("#([\w\*']+)([^\w\*']*)#",$argv[1],$m,2);foreach($m as list(,$a,$b)){$e=strlen(strtr($a,["'"=>""]))+$d;if(!$d=strstr($a,'*')?$e%2*10:0)echo chr($e+64),strpos(".$b","
")?". ":(strpos(".$b"," ")?" ":"");}echo".";
Save to file, rund php <scriptpath> <text>. Escape newlines in the text to make it work in shell.
-
1\$\begingroup\$ Can you give some instructions on running this? It looks like it reads input from
$argv[1], but I don't know how that approach will work when input contains newlines. I tried"Neurotic Frogs *O*ught To Re*a*x In *M*ud Baths!"as a command-line argument and gotIFHCHCFF.for output (as well as anUndefined variable: dwarning). \$\endgroup\$DLosc– DLosc2016年09月11日 03:59:15 +00:00Commented Sep 11, 2016 at 3:59 -
\$\begingroup\$ @DLosc: That notice (not a warning) should not be there with default settings. The easiest way is to prepend
<?, save it to a file and call that withphp <filename> <string>. I may have to add 2 to the byte count. \$\endgroup\$Titus– Titus2016年09月11日 17:19:32 +00:00Commented Sep 11, 2016 at 17:19 -
\$\begingroup\$ @Titus If you begin with
<?, you can also end with?>., for a net gain for 1. FWIW, I getIFHCMFF.for the first test case (using PHP 5.5.21 64-bit, VC14). Using$argnwith-Fmay also be an option. \$\endgroup\$primo– primo2016年09月12日 01:21:10 +00:00Commented Sep 12, 2016 at 1:21 -
\$\begingroup\$ What I mean is, I don't see how
php <filename> <string>is possible when<string>can contain newlines. \$\endgroup\$DLosc– DLosc2016年09月12日 03:43:39 +00:00Commented Sep 12, 2016 at 3:43 -
\$\begingroup\$ @DLosc: fixed the bug. For the newlines: escape them. \$\endgroup\$Titus– Titus2016年09月26日 12:04:55 +00:00Commented Sep 26, 2016 at 12:04