32
\$\begingroup\$

This challenge is inspired by one of my other challenges: Pointlessly make your way down the alphabet

That challenge asked you to take a string of text, and for each letter produce a sequence of letters moving down the alphabet to 'a'.

For this challenge I'd like to decode the output of that challenge (minus the line breaks), to find a human-readable string from a much longer human readable string.


Here is some text which has been encoded using the Dreaded Alphabet Cypher™️

hgfedcbaedcbalkjihgfedcbalkjihgfedcbaonmlkjihgfedcba wvutsrqponmlkjihgfedcbaonmlkjihgfedcbarqponmlkjihgfedcbalkjihgfedcbadcba

It consists of descending alphabetical sequences, ending in an 'a'. The first character in each of these sequences is a letter in the decoded text. (A space is still a space).

The challenge

  • Write code to convert a Dreaded Alphabet Cypher™️ encoded string into a decoded, human-readable string.
  • No need to validate if it is a Dreaded Alphabet Cypher™️ string, I'm not interested in how it handles invalid input.
  • Use any language you please.
  • Please include a link to an online iterpreter.
  • The input will only consist of lower-case letters and spaces. No upper cases, punctuation etc.
  • Code golf, try to write short code, standard loopholes etc.

Test Cases

Input:

hgfedcbaedcbalkjihgfedcbalkjihgfedcbaonmlkjihgfedcba wvutsrqponmlkjihgfedcbaonmlkjihgfedcbarqponmlkjihgfedcbalkjihgfedcbadcba

Output:

hello world

Input:

abacbadcbaedcbafedcbagfedcbahgfedcbaihgfedcbajihgfedcbakjihgfedcbalkjihgfedcbamlkjihgfedcbanmlkjihgfedcbaonmlkjihgfedcbaponmlkjihgfedcbaqponmlkjihgfedcbarqponmlkjihgfedcbasrqponmlkjihgfedcbatsrqponmlkjihgfedcbautsrqponmlkjihgfedcbavutsrqponmlkjihgfedcbawvutsrqponmlkjihgfedcbaxwvutsrqponmlkjihgfedcbayxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba

Output:

abcdefghijklmnopqrstuvwxyz

(beware the multiple a's)

Input:

aarqponmlkjihgfedcbaonmlkjihgfedcbanmlkjihgfedcba a aarqponmlkjihgfedcbaonmlkjihgfedcbanmlkjihgfedcbasrqponmlkjihgfedcbaonmlkjihgfedcbanmlkjihgfedcba hgfedcbaasrqponmlkjihgfedcba anmlkjihgfedcba aarqponmlkjihgfedcbadcbavutsrqponmlkjihgfedcbaarqponmlkjihgfedcbakjihgfedcba

Output:

aaron a aaronson has an aardvark

Input:

hgfedcbaaponmlkjihgfedcbaponmlkjihgfedcbayxwvutsrqponmlkjihgfedcba gfedcbaonmlkjihgfedcbalkjihgfedcbafedcbaihgfedcbanmlkjihgfedcbagfedcba

Output (is a secret, shh)

\$\endgroup\$
  • \$\begingroup\$ May the input contain leading or trailing spaces, or several spaces in a row? \$\endgroup\$ – Arnauld Nov 29 at 14:17
  • \$\begingroup\$ @Arnauld I'm happy with trailing and leading spaces, but not within the result itself. \$\endgroup\$ – AJFaraday Nov 29 at 14:19
  • \$\begingroup\$ Sorry, I meant the input, not the output. (I've edited my previous comment.) \$\endgroup\$ – Arnauld Nov 29 at 14:20
  • \$\begingroup\$ @Arnauld The input could have trailing and leading spaces. \$\endgroup\$ – AJFaraday Nov 29 at 16:43
  • 1
    \$\begingroup\$ @JonathanAllan sounds fine to me \$\endgroup\$ – AJFaraday Nov 29 at 19:12

36 Answers 36

15
\$\begingroup\$

JavaScript (ES9),  37 33  26 bytes

Returns an array of characters.

Either a negative look-behind:

s=>s.match(/(?<![b-z])./g)

Try it online!

Or a positive look-behind:

s=>s.match(/(?<=a| |^)./g)

Try it online!


25 bytes

If the input is guaranteed not to contain leading or double spaces (or we're allowed to remove and collapse them respectively), the positive look-behind can be made 1 byte shorter:

s=>s.match(/(?<=a|\b)./g)

Try it online!

\$\endgroup\$
12
\$\begingroup\$

Haskell, 32 bytes

f s=[y|(x,y)<-zip('a':s)s,x<'b']

Try it online!

For each pair of consecutive characters, if the first one is 'a' or ' ', output the second one. Prepends an 'a' to the string so that the first character is always output.

\$\endgroup\$
10
\$\begingroup\$

Jelly, 6 bytes

OŻI‘Tị

A monadic Link accepting a list of characters which yields a list of characters.

Try it online!

How?

OŻI‘Tị - Link: list of characters, S   e.g. "  baadcba  acbaedcba  "
                                       i.e. [' ',' ','b','a','a','d','c','b','a',' ',' ','a','c','b','a','e','d','c','b','a',' ',' ']
O      - to ordinal (vectorises across S)   [ 32, 32, 98, 97, 97,100, 99, 98, 97, 32, 32, 97, 99, 98, 97,101,100, 99, 98, 97, 32, 32]
 Ż     - prepend a zero                 [  0, 32, 32, 98, 97, 97,100, 99, 98, 97, 32, 32, 97, 99, 98, 97,101,100, 99, 98, 97, 32, 32]
  I    - incremental differences            [ 32,  0, 66, -1,  0,  3, -1, -1, -1,-65,  0, 65,  2, -1, -1,  4, -1, -1, -1, -1,-65,  0]
   ‘   - increment (vectorises)             [ 33,  1, 67,  0,  1,  4,  0,  0,  0,-64,  1, 66,  3,  0,  0,  5,  0,  0,  0,  0,-64,  1]
    T  - truthy indices                     [  1,  2,  3,      5,  6,             10, 11, 12, 13,         16,                 21, 22]
     ị - index into (S)                     [' ',' ','b',    'a','d',             ' ',' ','a','c',       'e',                ' ',' ']
                                       i.e. "  bad  ace  "
\$\endgroup\$
7
\$\begingroup\$

Perl 5 (-p), 15, 16 bytes

s/[b-z]\K.*?a//g

Try it online!

or using options trick

Perl 5 (-nF/(?<![\x20a])(?!^)[b-z]*a/), 7 bytes

print@F

Try it online!

\$\endgroup\$
7
\$\begingroup\$

Python 3, 43 bytes

lambda s:[r for l,r in zip(' '+s,s)if'b'>l]

An unnamed function accepting a string which returns a list of characters.

Try it online!


To accept & return lists use ...zip([' ']+s... instead (45)

To accept & return strings use lambda s:''.join(r for l,r in zip(' '+s,s)if'b'>l) (50)

\$\endgroup\$
6
\$\begingroup\$

05AB1E, 9 8 bytes

-1 byte thanks to Kevin Cruijssen

AηíR¬S.:

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ 8 bytes by replacing ASR with ¬S. \$\endgroup\$ – Kevin Cruijssen Nov 29 at 14:47
  • \$\begingroup\$ @KevinCruijssen Nice catch, thanks! \$\endgroup\$ – Grimmy Nov 29 at 14:54
6
\$\begingroup\$

C (clang), 100 98 96 95 94 92 90 88 64 46 bytes

Saved 24 bytes thanks to @Neil!!!
Saved 18 bytes thanks to @dingledooper (removed #include<stdio.h>)

f(char*c){while(*c)for(putchar(*c);*c++>97;);}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Why not just while(*n>97)? \$\endgroup\$ – Neil Nov 30 at 20:41
  • \$\begingroup\$ Also, by putting putchar(*c); before the while loop, I was able to replace all uses of n with c. \$\endgroup\$ – Neil Nov 30 at 20:46
  • \$\begingroup\$ In fact, by changing the while loop into a for loop you can move the putchar(*c) into the initialiser, saving you a pair of {}s. \$\endgroup\$ – Neil Nov 30 at 20:47
  • \$\begingroup\$ @Neil Thanks so much, that's amazing! And now I can finally have a post-increment as part of the condition expression. :-) \$\endgroup\$ – Noodle9 Nov 30 at 22:26
  • \$\begingroup\$ Thanks for posting this; the loop structure was useful for an x86 machine code implementation \$\endgroup\$ – Peter Cordes Dec 2 at 6:48
6
\$\begingroup\$

J, 22 13 bytes

–9 bytes, thanks @Jonah!

#~1,98>3 u:}:

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ 13 bytes: #~1,98>3 u:}: (Try it online!) \$\endgroup\$ – Jonah Dec 1 at 13:15
6
\$\begingroup\$

Japt -g, 10 9 8 bytes

óÈ¥YcÄÃy

Try it

ó           split between those characters that return true when passed through...
 È    Ã     function :
  ¥         x equal to..
   YcÄ      character next to y
       y   transpose

Flag -g used to get 1st element 

Saved 1 thanks to @Shaggy

\$\endgroup\$
  • 1
    \$\begingroup\$ 1dYc can be YcÄ \$\endgroup\$ – Shaggy Nov 29 at 16:47
  • \$\begingroup\$ I don't know why ó> or ò< won't work here; was trying them earlier. \$\endgroup\$ – Shaggy Nov 29 at 16:49
  • \$\begingroup\$ I was trying something similar too but nothing worked \$\endgroup\$ – AZTECCO Nov 29 at 17:30
  • \$\begingroup\$ And yet ... petershaggynoble.github.io/Japt-Interpreter/… \$\endgroup\$ – Shaggy Nov 29 at 18:35
  • \$\begingroup\$ @Shaggy i moved to japt chat \$\endgroup\$ – AZTECCO Nov 29 at 19:32
5
\$\begingroup\$

R, 57 56 bytes

-1 byte thanks to Giuseppe.

function(s)intToUtf8(c(v<-utf8ToInt(s),0)[c(0,v)%%32<2])

Try it online!

Could probably be made shorter with a regex, but this is more R-like. Converts to integers (vector v), then keeps only the first character and the characters which follow a space or an a. Since the corresponding codepoints are 32 and 97, we can check whether \$v_i\mod 32 <2\$ to get the relevant entries.

\$\endgroup\$
  • 1
    \$\begingroup\$ 56 bytes using in-line assignment instead of a function argument \$\endgroup\$ – Giuseppe Dec 2 at 20:57
  • \$\begingroup\$ @Giuseppe Thanks! \$\endgroup\$ – Robin Ryder Dec 2 at 21:09
4
\$\begingroup\$

Haskell, 59 bytes

f""=""
f(x:s)|x<'b'=x:(f s)|1>0=x:(f$tail$dropWhile(>'a')s)

Try it online!

No match for regex solutions, but I figured I'd try a recursive solution.

\$\endgroup\$
4
\$\begingroup\$

JavaScript, 35 32 31 bytes

s=>s.replace(/ |.*?a/g,x=>x[0])

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Ruby, 28 bytes

->s{s.scan(/(?<=a|\b)./)*""}

Try it online!

This is a port of Arnauld's Javascript answer to Ruby.

\$\endgroup\$
4
\$\begingroup\$

Japt, 9 bytes

Outputs an array of characters.

óÈkSiaîÎ

Try it

óÈkSiaîΠ    :Implicit input of string
ó             :Partition after characters that return falsey (empty string)
 È            :When passed through the following function
  k           :  Remove all characters in
   S          :    Space
    ia        :    Prepended with "a"
      Ã       :End function
       ®      :Map
        Î     :  First character
\$\endgroup\$
4
\$\begingroup\$

C (clang), 66 48 bytes

edit: -18 bytes thanks to @dingledooper (removed #include)

P;D(char*T){*T?P>97||putchar(*T),P=*T++,D(T):0;}

Try it online!

int P;          // previous character
D(char*T)
{
    if (*T)     // check for end of string
    {
        if (!(P > 'a'))     // output current if previous was 'a' or ' '
            putchar(*T);
        P = *T;             // update previous
        D(T + 1);           // find next
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ You can remove the #include <stdio.h>, by the way \$\endgroup\$ – dingledooper Nov 30 at 18:45
  • \$\begingroup\$ @dingledooper that's interesting, I didn't know that C allows calling standard functions without including the appropriate header. \$\endgroup\$ – xibu Nov 30 at 19:11
  • \$\begingroup\$ @xibu: It's not recommended, but yes C89 has default-promotion rules for what happens when calling a function for which no prototype exists. If the actual definition of the function is compatible with the args you passed it, it works. If not, UB with no detection at compile time of the mismatch. \$\endgroup\$ – Peter Cordes Dec 2 at 2:51
3
\$\begingroup\$

Jelly, 10 bytes

Ḳµ=Ṃk⁸ZḢ)K

Try it online!

A monadic link taking a Jelly string and returning a Jelly string. When run as a full program takes a string as its argument and implicitly prints the output string.

\$\endgroup\$
3
\$\begingroup\$

SNOBOL4 (CSNOBOL4), 106 bytes

	I =INPUT
S	I LEN(1) . X	:F(O)
	O =O X
	R =
	REVERSE(&LCASE) X REM . R
	I X R REM . I	:(S)
O	OUTPUT =O
END

Try it online!

	I =INPUT			;* Read input
S	I LEN(1) . X	:F(O)		;* extract the first letter or goto O if none exist
	O =O X				;* append that letter to the output
	R =				;* set R to the empty string
	REVERSE(&LCASE) X REM . R	;* Set R to the letters up to 'a', starting at X.
					;* R is empty string if X is space or 'a'
	I X R REM . I	:(S)		;* Set the REMainder of I after (X + R) to I; goto S
O	OUTPUT =O			;* output the string
END
\$\endgroup\$
3
\$\begingroup\$

Red, 74 bytes

func[s][parse s[any[copy t thru"a"(prin#"`"+ length? t)opt[sp(prin sp)]]]]

Try it online!

\$\endgroup\$
3
\$\begingroup\$

braingasm, 20 18 bytes

,[.32-n[65-$[,]],]

It's been a while since I worked on braingasm. There seems to be a compare function, but I guess I didn't come around to implement a way to use the result. Well, this works:

,                    #  Read a byte from input
 [               ]   #  While the current byte is not zero
  .                  #    Print it
   32-               #    Decrease by 32
      n[       ]     #    If the result is non-zero (meaning it wasn't a space):
        65-          #      Subtract 65, leaving 'a' => 0, 'b' => 1, 'c' => 3, etc
           $[,]      #      That many times, read a byte from input
                ,    #    Read another byte

Doesn't handle newlines or anything other than lowercase letters and spaces in the input.

\$\endgroup\$
3
\$\begingroup\$

Pip, 12 bytes

aR`\w*?a`_@0

Try it online!

Explanation

a             The input, as a command-line argument
 R            Do a regex replacement:
  `\w*?a`      Match a non-greedy series of letters ending in a
         _@0   Replace with the first character of the match
\$\endgroup\$
3
\$\begingroup\$

C# (Visual C# Interactive Compiler), 33 bytes

x=>x.Where((_,l)=>l<1||x[l-1]<98)

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Keg, -ir, -lp, 9 7 bytes

(⑩(a-|_

Try it online!

2 bytes saved thanks to @A̲̲ suggesting to use a for loop over a while loop

Answer History

{!|⑩(a-|_

Try it online!

{!|⑩(a-|_
{!|             #While there are still items on the stack:
   ⑩            #Print t.o.s without popping
    (a-|            #Subtract the code point of a from tos and repeat that many times:
        _           #Pop items off the stack
\$\endgroup\$
  • \$\begingroup\$ 7 bytes. A simple optimization from while to for. (Your current program also generates an error in the end, so..) \$\endgroup\$ – A̲̲ Nov 30 at 12:34
  • \$\begingroup\$ @A No, not at all. I... I wouldn't have thought of using a for loop... I was going to add a 'loop while there are still items on the stack' command to get this down to 6 bytes. \$\endgroup\$ – Jono 2906 Nov 30 at 21:28
3
\$\begingroup\$

C++, 464 bytes

I'm still a beginner so will be grateful for any advice.

#include <iostream>
#include <string>
using namespace std;

int main() {
  string input , output;
  getline(cin,input);
  output = input[0];

  for (int i = 0 ; i<input.size() ; i++) {
      if (input[i] == 'a')  {
          output += input[i+1];
      }  else if (input[i] == ' ') {
          output += input[i+1];
      }  else {
          continue;
      }

  }
  cout << output;
}

Try it online!

New contributor
Dorrineeee is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
  • 1
    \$\begingroup\$ I don't know much about C++, but 212 bytes \$\endgroup\$ – Jono 2906 Dec 1 at 5:37
  • \$\begingroup\$ Also, welcome to CGCC! I hope you enjoy golfing answers to challenges while you're here! \$\endgroup\$ – Jono 2906 Dec 1 at 5:38
  • \$\begingroup\$ Get a warning for 159 bytes \$\endgroup\$ – A̲̲ Dec 1 at 7:44
  • \$\begingroup\$ As a first step, definitely remove all whitespace (including newline) and unneeded {}. And always use single-letter variable names. You can include an "ungolfed" version of your code in your answer (with newlines and comments) so people can read it. See also Tips for golfing in C++ \$\endgroup\$ – Peter Cordes Dec 2 at 2:55
  • 1
    \$\begingroup\$ The <string> header is already included in <iostream>, the number of times you need std:: is way less bytes than using namespace std;, std::string::operator[](size_t pos) will return '\0' for pos == size(). and your if statement can be collapsed to checking if the current character is less than 98('b'). 122 bytes. :-) \$\endgroup\$ – Noodle9 Dec 2 at 14:27
3
\$\begingroup\$

x86 machine code, 12 bytes

(Runs in any mode, 16-bit, 32-bit, or 64-bit.)

This is based on @Noodle9's C algorithm, but with rearrangement into do{}while asm loop structure.

NASM listing: address, machine code (hex dump of the actual answer), source

 1                             decda:  ; (char *dst=RDI, const char *src=RSI)
 2 00000000 AC                     lodsb             ; AL = *src++
 3                             .loop:                ; do{
 4 00000001 AA                     stosb                 ; *dst++ = AL
 5                                .skip_until_a:         ; do{
 6 00000002 3C61                    cmp  al, 'a'
 7 00000004 AC                      lodsb
 8 00000005 77FB                    ja  .skip_until_a    ; }while((AL=*p++) > 'a')
 9 00000007 84C0                   test al,al
10 00000009 75F6                   jnz .loop         ; } while(AL != terminator)
11                             ;    stosb             ; append a terminating 0
12 0000000B C3                     ret
                       ; output end pointer returned in RDI

Try it online!

The input is an implicit-length (0-terminated) C string. The output is an explicit-length buffer. This function returns (in RDI) a pointer to one-past-the-end.

(The caller already knows where the output buf starts, so it has C++-like .begin and .end iterators). Making this function append a terminating 0 would cost 1 extra byte for another stosb after the loop, unless there's a way to rearrange things that I'm not seeing.


A sample caller that passes the output of decda(argv[1]) to a write system call is included in the TIO link. You can inc edx in that caller to prove that the output didn't over-write beyond where it's supposed to.

I had been thinking of using loop .loop instead of test al,al/jnz but then the inner loop would need a dec ecx or something. (Without that, it loops until writing RCX output bytes, which only works if the caller knows the decrypted length, not the source length.) Worth considering for a 32-bit-only version that can use 1-byte dec ecx, unless that could reach loop or loopne with ECX<=0 for valid inputs.

\$\endgroup\$
  • \$\begingroup\$ Very nice! I assembled and tested this on 16-bit MASM 1.1 and passes all tests perfectly. :) \$\endgroup\$ – 640KB 22 hours ago
  • 1
    \$\begingroup\$ @640KB: cool, I only tested it on x86-64 Linux. Well-golfed machine code is often independent of mode thanks to using implicit increments and sizes so I was pretty sure about my claim of working in any mode, but nice to have it tested :) \$\endgroup\$ – Peter Cordes 22 hours ago
3
\$\begingroup\$

K (oK), 14 bytes

{x@&1,98>-1_x}

Try it online!

Inspired by FrownyFrog's/Jonah's J solution

\$\endgroup\$
2
\$\begingroup\$

C# (Visual C# Interactive Compiler), 69 bytes

Hard to beat Regex on this one

s=>System.Text.RegularExpressions.Regex.Replace(s,"([b-z]).*?a","$1")

Try it online!

Returns an array of Matches (characters), 64 bytes

s=>System.Text.RegularExpressions.Regex.Matches(s,"(?<![b-z]).")

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Jelly, 20 bytes

O0;ṡ2_/>0ƊÐḟṪ€Ọ
ḲÇ€K

Try it online!

probably too long lol

Explanation

O0;ṡ2_/>0ƊÐḟṪ€Ọ  This challenge, for one word
O                turn all the chars to their ASCII forms
 0;              prepend 0
   ṡ2            split into (overlapping) slices of length 2 (pairs)
          Ðḟ     filter: remove elements where
     _/          reducing over subtraction (which is equivalent to the first minus the second, in each pair)
       >0        is greater than 0 (if it's strictly descending)
            Ṫ€   take the second element of each pair
              Ọ  convert back to chars
ḲÇ€K             Compiled Program
Ḳ                split on spaces
 ǀ              apply the helper to each word
   K             join on spaces
\$\endgroup\$
2
\$\begingroup\$

Python 2, 87 86 84 bytes

lambda i:"".join(x[:2]if'a'>x else x[0]for x in i.replace('a','a#').split('#')[:-1])

Try it online!

Non-regex effort. Replaces a with a plus a disposable character then splits on the disposable character and outputs either the first or first two letters of each list element depending on whether the first is a space or not.

\$\endgroup\$
2
\$\begingroup\$

Ruby, 20 + 1 bytes

gsub /(?<!a|\b)./,''

1 extra byte for the p command line option:

$ ruby -p alphabet_cypher.rb <<< "hgfedcbaedcbalkjihgfedcbalkjihgfedcbaonmlkjihgfedcba wvutsrqponmlkjihgfedgfedcbarqponmlkjihgfedcbalkjihgfedcbadcba"
hello world

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 8 bytes

Φ苧θ⊖κb

Try it online! Link is to verbose version of code. Explanation:

 θ          Input string
Φ           Filtered where
    θ       Input string
   §        Cyclically indexed by
      κ     Current index
     ⊖      Decremented
  ‹         Is less than
       b    Literal string `b`
            Implicitly print
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.