Morse code golf

You can read the title of this post as ((Morse code) golf) or as (Morse (code golf)).

Morse code is a sort of approximate Huffman coding of letters: letters are assigned symbols so that more common letters can be transmitted more quickly. You can read about how well Morse code achieves this design objective here.

But digits in Morse code are kinda strange. I imagine they were an afterthought, tacked on after encodings had been assigned to each of the letters, and so had to avoid encodings that were already in use. Here are the assignments:

    |-------+-------|
    | Digit | Code  |
    |-------+-------|
    |     1 | .---- |
    |     2 | ..--- |
    |     3 | ...-- |
    |     4 | ....- |
    |     5 | ..... |
    |     6 | -.... |
    |     7 | --... |
    |     8 | ---.. |
    |     9 | ----. |
    |     0 | ----- |
    |-------+-------|

There’s no attempt to relate transmission length to frequency. Maybe the idea was that all digits are equally common. While in some contexts this is true, it’s not true in general for mathematical and psychological reasons.

There is a sort of mathematical pattern to the Morse code symbols for digits. For 1 ≤ n ≤ 5, the symbol for n is n dots followed by 5-n dashes. For 6 ≤ n ≤ 9, the symbol is n-5 dashes followed by 10-n dots. The same rule extends to 0 if you think of 0 as 10.

A more mathematically satisfying way to assign symbols would have been binary numbers padded to five places:

    0 -> .....
    1 -> ....-
    2 -> ..._.
    etc.

Because the Morse encoding of digits is awkward, it’s not easy to describe succinctly. And here is where golf comes in.

The idea of code golf is to write the shortest program that does some task. Fewer characters is better, just as in golf the lowest score wins.

Here’s the challenge: Write two functions as small you can, one to encode digits as Morse code and another to decode Morse digits. Share your solutions in the comments below.

Related posts

18 thoughts on “Morse code golf

  1. def encode(n):
        return "-----.....-----"[10-n:15-n]
    
    def decode(m):
        return (10-"-----.....-----".find(m))%10
    

    I suppose that the Morse encoding for numbers may be related with a mechanical rotary encoder.

  2. Julien Grenier
    S="-"*4+"."*5+"-"*5
    def e(x):
        yield S[9-x:14-x]
    
    def d(s):
        yield 9-S.find(s)
    
  3. I tried
    – not to rely on libraries (for example, string match functions can decode morse code with minimal added code)
    – not to keep the solution space in memory (though even the compiled code that contains the full solution space is actually space efficient).

    int decode_morse(char *buf) {
            int i;
            for(i=1; buf[i] == buf[0] && ++i <= 5;);
            return ((buf[0] == '.' ? 0:5) + i) % 10;
    }
    
    void encode_morse(int n, char *buf) {
            for(int i=0;i<5;i++) {
                    buf[i]= (10 - n + i) / 5 % 2 == 1 ? '.':'-';
            }
    }
    
  4. Just for fun, in AWK:

    function decode(a){return (10-index("----.....----",a))%10}
    function encode(a){ return substr("-----.....----",(11-a)%11,5)}
    
  5. apl:
    1. e←{5↑⌽(1-⍵)’.—–….’}
    so to generate [0,9] –> e¨0,⍳9
    2. d←{¯1+(↑e¨0,⍳9)⍳⍵}

    ‘¨’ is the each operator, so d ¨e ¨0,⍳9 returns 0, 1, … ,8 ,9

  6. I think you have a small typo in describing 6-9 as being some dashes followed by some more dashes.

    And I wouldn’t have commented just for this, but putting the 0 at the end is bugging me, since 0 < 1 and the rest of the list is in numerical order. Keyboards and telephone keypads also getting it wrong is annoying.

    Having commented on the placement of 0, however, I am led to note that 0≤n<5 are represented by n dots followed by 5-n dashes; and 5≤n<10 are represented by n-5 dashes followed by 10-n dots.

  7. implemented without string searches, that do a lot of heavy lifting and can have complex state machine code, and with some test cases:

    int decode_morse(char *buf) {
        int i=1;
        while(buf[i] == buf[0] && ++i <= 5);
        return ((buf[0] == '.' ? 0:5) + i) % 10;
    }
    
    void encode_morse(int n, char *buf) {
        for(int i=0;i<5;i++) {
            buf[i]= (10 - n + i) / 5 % 2 ? '.':'-';
        }
    }
    
    int main (int argc, char ** args) {
        char buf[] = "\0\0\0\0\0\0";
        for(int i=0; i  %d\n",i, decode_morse(buf));
        printf("done.\n");
    }
    
  8. In amateur radio usage numbers are often abbreviated by omitting all but one dash, so 1 becomes A (.-), 2 becomes U (..-), and so on, but only where it is clear they are numbers.

    This turns the common signal strength and quality report of 599 into 5NN, and 1234567890 into AUV456BDNT. Five may also be shortened to one dot (E).

    Googling around I also found “AUWVSBGDNT” mentioned as a set of cut numbers, but I hadn’t heard of that one before.

    Using binary encoding would give seven three dashes. By giving the positions values “54321” one could encode all numbers with zero to two dashes:

    ….., …._, …_., .._.., ._…, _…., _…_, _.._., _._.., __…

    As a bonus this encoding could be extended to cover hexadecimal digits also.

  9. e = lambda n: "".join("-."[n-6<i<n] for i in range(5))
    d = lambda s: len(s.rstrip(s[-1]))+5*(s[-1]==".")
    
  10. Slightly longer alternative for encoding using a linear function, integer division and -1, 0, 1 indexing into string of length 2.

    e = lambda n: "".join(".-"[(n-i-1)//5] for i in range(5))

    Inneficient, but general approach for decoding using a given encoding function.

    d = lambda s: [ i for i in range(10) if e(i)==s ][0]

    A similar approach also works for encoding using a given decoding function.

  11. Private static string Encoder(int i)
    {
         return ".....-----.....".Substring(10-i, 5);
    }
    
    Private static int Decoder(string s)
    {
         Return 9-"....-----.....".IndexOf(s);
    }
    
  12. Another version in python:

    def decode(m):
      return (5+ m.count("-"))%10 if m.startswith("-") else m.count(".")
    
    def encode(i):
      return "."*i + "-"*(5-i) if i < 6 else "-"*(i-5) + "."*(10-i)
    
  13. Here’s an approach that works just for the decode function. Only 28 characters, in Python 2 (doesn’t work in Python 3 as the hash isn’t deterministic). I searched over the Cartesian product of `string.printable` with itself to find one of the shortest salts that worked.

    d=lambda c:hash(c+'BvS+')%10

Comments are closed.