Retrograde morse (d)e(n)coder

6

0

This question is inspired by retrograde music. Retrograde music is a piece of music that can both be played normally (from the beginning to the end) as reversed (from the end the beginning). A similar thing exists in literature, this is called a semi-palindrome, e.g. "roma" and "amor". Your task is to write a retrograde program. If executed normally it has to encode a string with the morse code, decode it when executed reversed.

Task

You're to write a program or function that accepts a string and outputs it's morse code encoding (STDIN and STDOUT are used in case of a program, a function receives it's input as a function argument and returns a string). However, when you run the reverse of the program, it works the same except it decodes the input. Note that you don't have to use the same programming language. The reversing of the program is done manually.

Details

  • Comments are not allowed in your program, as they would make this task very easy.
  • You have to use the international morse code. This includes letters, numbers and some punctuation. See the link for a complete list. No prosigns or special characters have to be supported. There's one exception mentioned in the edit.
  • You have to represent dots and dashes with . and -.
  • Letters are separated with (spaces), words are separated with / (slashes).
  • You can choose to use either use lower or uppercase. However, you have to be consistent (if your program only uses uppercase, the reversed program must do so too).
  • You can assume the program will only receive valid input.

Example

Input of the normal program or output of the reversed program:

MORSE CODE?

Output of the normal program or input of the reversed program:

-- --- .-. ... . / -.-. --- -.. . ..--..

Score

Because I want to reward both clever (short) algorithms and creativity, Your score will be based upon your votes and the program length. Concretely, it will be 100 * amount of votes / program length (in bytes)

EDIT: The morse code site I mentioned uses the same code for opening and closing parentheses, however it's not my intention to let you implement a feature for recognizing this. So you have to use these more modern morse codes for the parentheses:

( = -.--.
) = -.--.-

Def

Posted 2015-01-08T16:17:38.410

Reputation: 602

Answers

3

CJam, 349 284 bytes

This is the encoder from plaintext to International Morse code:

L;"q['[,65>A,841306689079511650 64b:c]s"~"*S/as/'res]c:b46 056115970986603148,A>56,['[/,'c:+f34b4i%Ws;\\";L
2750763932992480612976738964894343100372667569857057816704667471637641979761595859190213443543359288493237588430216583759463627409436875398907
L;"\\;4b43f+:c',/erS/'/*S*"~"/Sq";L

The reverse string of which is:

L;"qS/"~"*S*/'/Sre/,'c:+f34b4;\\";L
7098935786349047263649573856120348857323948829533453443120919585951679791467361747664076187507589657662730013434984698376792160842992393670572
L;"\\;sW%i4b43f+:c',/['[,65>A,841306689079511650 64b:c]ser'/sa/S*"~"s]c:b46 056115970986603148,A>56,['[q";L

which decodes an International Morse Code representation back to plain text.

(New lines just for show)

This supports capital alphabets A-Z, number 0-9 and .,:?'-/()" punctuation marks, where Morse Code for ( and ) is same.

How it works:

The trick here is simple, CJam supports evaluation of any random string as a code using the operator ~. So if you have something like "code in a string"~"another code in a string", then when running normally, the first string will be evaluated while the second one will remain a string. But when running the reverse of the code, the second string will be evaluated, while the first will remain a string.

So now you just have to make sure that you have your code in the above format along with a logic to remove the second string while running in normal fashion and the first string while running the reverse code. This gives us something like:

];"code for encoding to morse"~"reverse of code for decoding from morse";]

whose reverse will be

];"code for decoding from morse"~"reverse of code for encoding to morse";]

This works like:

];                                "Create an empty array and pop it from stack";
  "code 1"~                       "Put a string with code 1 and evaluate it";
           "code 2";              "Put the string for code 2 and pop it from stack";
                    ]             "Wrap everything in an array. Noop as last operation";

The reverse of it is just code 1 replaced by reverse of code 2 and vice-versa, so it works in a similar manner.

Now the actual code which encodes/decodes is a simple transliteration from the plaintext to morse and vice-versa.

Try it online here

Optimizer

Posted 2015-01-08T16:17:38.410

Reputation: 25 836

A string followed by a semicolon is the standard CJam comment, so I think this falls foul of the prohibition "Comments are not allowed in your program, as they would make this task very easy." – Peter Taylor – 2015-01-09T11:09:25.783

@PeterTaylor its not a standard CJam comment. CJam does not have comments, so the only option to simulate comments is a string followed by a semi colon. Also, that is just the idea of the approach, I can achieve similar results by 0* too – Optimizer – 2015-01-09T11:12:01.313

Well, I'll let you make a counter-case rather than play devil's advocate myself.

– Peter Taylor – 2015-01-09T11:46:18.850

@Deformyer from your edit : So you can use these more modern morse codes for the parentheses . It does not say that everyone has to put this new morse code for ). Sounds more like an optional thing to me. – Optimizer – 2015-01-09T21:45:06.023

@Deformyer I don't see a point in adding a compulsory rule so late. I have changed it for now, but any further rule changes will simply make me delete my answer. – Optimizer – 2015-01-09T21:55:20.247

@Optimizer I'm sorry but after encoding and decoding, () would become ((, this seemed wrong to me so I made an edit. – Def – 2015-01-09T22:01:27.350

Look ma, no eval! :) – Ilmari Karonen – 2015-01-11T22:23:42.330

4

Perl 5: too long to care (740 though)

Just wanted to see if it will be possible to write this in a language with non single character statements. The result is not pretty, but works. Most of the code is used in both decoder and encoder, but the eval+'{code}' trick shows a loophole which could be used to write completely separate code for decoder and encoder.

Fixed all known problems. Supports both lower and upper case input with all punctuation characters mentioned on the page linked in the question.

Encoder:

;;$^;s,.*,reverse+uc,e;
tr,+?.(),+~=<>,+rt;
1while+s/E/#_/s+s/T/`_/s+s! !{_!s+elihw1;
1while+s/A/`#_/s+s/N/#`_/s+s/I/##_/s+s/M/``_/s+elihw1;
1while+s/O/```_/s+s/D/##`_/s+s/R/#`#_/s+s/S/###_/s
      +s/G/#``_/s+s/U/`##_/s+s/W/``#_/s+s/K/`#`_/s+elihw1;
1while+s/B/###`_/s+s/C/#`#`_/s+s/P/#``#_/s+s/Q/`#``_/s
      +s/F/#`##_/s+s/H/####_/s+s/V/`###_/s+s/J/```#_/s
      +s/X/`##`_/s+s/L/##`#_/s+s/Y/``#`_/s+s/Z/##``_/s+elihw1;
1while+s/1/````#_/s+s/2/```##_/s+s/3/``###_/s+s!<!#``#`_!s
      +s/4/`####_/s+s/5/#####_/s+s/6/####`_/s+s/7/###``_/s
      +s/8/##```_/s+s/9/#````_/s+s/0/`````_/s+s!/!#`##`_!s+elihw1;
1while+s/~/##``##_/s+s/=/`#`#`#_/s+s/,/``##``_/s
      +s/:/###```_/s+s/'/#````#_/s+s/"/#`##`#_/s
      +s/-/`####`_/s+s!>!`#``#`_!s+elihw1;
tr,+{`#_,+/\-\. ,+rt;
;;$^;s,.*,reverse,e;
s/ //s

Decoder:

s// /s
;e,esrever,*.,s;^$;;
;tr+, .\-\/+,_#`{+,rt
;1while+s!_`#``#`!>!s+s/_`####`/-/s+
s/_#`##`#/"/s+s/_#````#/'/s+s/_```###/:/s+
s/_``##``/,/s+s/_#`#`#`/=/s+s/_##``##/~/s+elihw1
;1while+s!_`##`#!/!s+s/_`````/0/s+s/_````#/9/s+s/_```##/8/s+
s/_``###/7/s+s/_`####/6/s+s/_#####/5/s+s/_####`/4/s+
s!_`#``#!<!s+s/_###``/3/s+s/_##```/2/s+s/_#````/1/s+elihw1
;1while+s/_``##/Z/s+s/_`#``/Y/s+s/_#`##/L/s+s/_`##`/X/s+
s/_#```/J/s+s/_###`/V/s+s/_####/H/s+s/_##`#/F/s+
s/_``#`/Q/s+s/_#``#/P/s+s/_`#`#/C/s+s/_`###/B/s+elihw1
;1while+s/_`#`/K/s+s/_#``/W/s+s/_##`/U/s+s/_``#/G/s+
s/_###/S/s+s/_#`#/R/s+s/_`##/D/s+s/_```/O/s+elihw1
;1while+s/_``/M/s+s/_##/I/s+s/_`#/N/s+s/_#`/A/s+elihw1
;1while+s!_{! !s+s/_`/T/s+s/_#/E/s+elihw1
;tr+,><=~+,)(.?+,rt
;e,cu+esrever,*.,s;^$;;

Usage

$ perl -lp retr.pl <<<"(Morse) Code?"
-.--. -- --- .-. ... . -.--.- / -.-. --- -.. . ..--..
$ perl -p0e '$_=reverse' retr.pl >rter.pl
$ perl -lp rter.pl <<<"-.--. -- --- .-. ... . -.--.- / -.-. --- -.. . ..--.."
(MORSE) CODE?

Explanation:

;;$^;s,.*,reverse+uc,e; # parses as $^; s/.*/reverse uc/e
                        # and in decoder as e; cu + esrever; *.; s/^$//
                        # reverse and uppercase input, no-op in decoder
tr,+?.(),+~=<>,+rt;     # parses as tr/+?()/+=<>/ + rt
                        # in decoder as tr/,><=~/,)(.?/ , rt
                        # equivalent to y/?.()/~=<>/ and y/><=~/)(.?/ in decoder
1while+s/T/`_/s+elihw1; # parses as while(+s/T/`_/+elihw1){1}
                        #        => while(s/T/`_/s+0){}
                        # equivalent to s/T/`_/g and s/_`/T/g in decoder
s/ //s                  # remove leading space / add leading space

nutki

Posted 2015-01-08T16:17:38.410

Reputation: 3 634

@Optimizer Obviously it can be easily expanded to support all characters that don't need escaping in a regexp. Just have to remember to start with shortest codes in the encoder, so decoder can work properly. – nutki – 2015-01-08T17:28:27.217

Could you add an explanation please? – Def – 2015-01-08T17:28:44.857

2@nutki I think you need to finish your program. The requirements say it needs to support the international morse code. – mbomb007 – 2015-01-08T19:45:44.767

How can it decode the same '-.--.-' to '(' and ')'? – edc65 – 2015-01-09T11:29:44.033

@edc65 Even occurrences are replaced with ( and odd with ). This works under the assumptions I stated. – nutki – 2015-01-09T11:33:44.073

@nutki I clarified this in the question – Def – 2015-01-09T11:58:26.063

2

CJam, 286 bytes, no eval

Original (Morse encoder):

L;"'*S/as/'res]c:b46 056115970986603148,A>56,['[/,'c:+f34b4i%Ws;\;";q['[,65>A,841306689079511650 64b:c]s'";L
2750763932992480612976738964894343100372667569857057816704667471637641979761595859190213443543359288493237588430216583759463627409436875398907
L;"'/Sq;";\;4b43f+:c',/erS/'/*S*'";L

Reversed (Morse decoder):

L;"'*S*/'/Sre/,'c:+f34b4;\;";qS/'";L
7098935786349047263649573856120348857323948829533453443120919585951679791467361747664076187507589657662730013434984698376792160842992393670572
L;"'s]c:b46 056115970986603148,A>56,['[q;";\;sW%i4b43f+:c',/['[,65>A,841306689079511650 64b:c]ser'/sa/S*'";L

This is just a trivial tweak of Optimizer's solution to eliminate the string eval (~), in response to a meta post asserting that "[the] use of eval statements is what makes this abuse possible". Accordingly, as it's mostly not my own code, I have marked it as Community Wiki.

Instead of Optimizer's trick of placing the eval operator ~ between two quoted strings, like this:

L;"encoder"~"reversed decoder";L

my version relies on the fact that, in CJam, any character following a single quote (') is interpreted as a character literal:

L;"'reversed decoder;";encoder'";L

Relevant CJam speed-tutorial: L is an empty string, "..." is a double-quoted string, '" is a literal double quote, and ; pops the most recent value off the stack. Thus, this code first pushes an empty string, pops it, pushes the string 'reversed decoder;, pops it, executes encoder, pushes another double quote, pops it, and pushes an empty string (which has no effect on the output).

Update: Saved 8 bytes by a trivial (in hindsight) optimization. With the further saving from eliminating needless backslashed, this is now just two bytes longer than Optimizer's original eval-based solution.

Ilmari Karonen

Posted 2015-01-08T16:17:38.410

Reputation: 19 513

1FYI, I have a 220 byte version ;) – Optimizer – 2015-01-11T23:03:59.740

@Deformyer: The thing is, even if you defined a comment loosely as "anything that allows easy unused code", I'm not sure what you'd ban to disallow the trick I describe above. Strings? Character literals? Popping elements off the stack? (Hint: The last one won't work; it's trivial to circumvent. In fact, I think I know a way to make this work without using any strings, either...) – Ilmari Karonen – 2015-01-12T14:53:09.387

@Deformyer: It's kind of hard to see what your criterion for "using a loophole" is, though, except maybe "doing anything that makes the challenge too easy", for some value of "too easy" primarily known to you. IMO, that makes the spec a moving target, or a "read my mind" challenge. In any case, my answer above doesn't "use any function that makes a string executable"; I believe I could even tweak it to not use any strings at all. – Ilmari Karonen – 2015-01-12T23:50:50.157

(The basic no-strings trick is something like L;{'redoced;};encoder'{;L, where the reversed decoder is wrapped in a code block that is parsed but never executed -- basically a CJam equivalent of if(0){...}. The main difficulty is that not all CJam code is syntactically valid when reversed, but it's not really a major hurdle, since most of it is.) – Ilmari Karonen – 2015-01-12T23:52:27.567