15
\$\begingroup\$

Context

We're at war! You need to transfer an important message to your general to inform them from an imminent assault. The thing is, your enemy knows a lot about cryptography : you will need to be more creative in order to encode your message...

Task

Create a program that accepts an ascii message as input, and outputs the encoded message. The message should be encoded this way : each character should get its ascii value shifted by its position in the message.

For example, let's encode the message "Hello, world!"

H   e   l   l   o   ,       w   o   r   l   d   !     Original message

72  101 108 108 111 44  32  119 111 114 108 100 33    ASCII value

0   1   2   3   4   5   6   7   8   9   10  11  12    Place in the message

72  102 110 111 115 49  38  126 119 123 118 111 45    Encoded ascii (line 1 + line 2)

H   f   n   o   s   1   &   ~   w   {   v   o   -     Encoded message

The message Hello, world! should be encoded as Hfnos1&~w{vo-.

Sometimes the encoded ascii can go beyond printable character. In that case, the value loop back to 32 (read the rules for additionnal informations)

T   h   e       r   a   n   d   o   m       g   u   y      Original message

84  104 101 32  114 97  110 100 111 109 32  103 117 121    ASCII value

0   1   2   3   4   5   6   7   8   9   10  11  12  13     Place in the message

84  105 103 35  118 102 116 107 119 118 42  114 129 134    Encoded ascii (line 1 + line 2)

84  105 103 35  118 102 116 107 119 118 42  114 34  39    Corrected encoded ascii (looped back to 32)

T   i   g   #   v   f   t   k   w   v   *   r   "   '

The random guy is then converted into Tig#vftkwv*r"'

Using the same strat, zzzzzzzzz will be converted into z{|}~ !"# (ascii values converted into 122 - 123 - 124 - 125 - 126 - 32 - 33 - 34 - 35)

Rules

  • The input message will be composed of printable ascii character (between 32 and 126)

  • The message should be encoded as described before.

  • The ascii values of the encoded message should be set between 32 and 126.

  • The first character of the message should be at position 0.

  • If the new ascii value goes beyond the limit, it should loop back to 32.

  • Every character should be encoded. This includes punctuation, spaces etc.

  • No standard loopholes allowed.

  • This is codegolf, so the shortest code wins.

\$\endgroup\$
8
  • \$\begingroup\$ Meta \$\endgroup\$ Jun 3, 2020 at 7:44
  • 2
    \$\begingroup\$ Suggested test case: 'zzzzzzzzz', to catch the correct wrapping point \$\endgroup\$
    – PaMu
    Jun 3, 2020 at 8:11
  • \$\begingroup\$ @Adám yes, every character in the input message will be set between 32 and 126 \$\endgroup\$ Jun 3, 2020 at 8:15
  • 2
    \$\begingroup\$ You may want to spell out that your positions are 0-based. \$\endgroup\$
    – Adám
    Jun 3, 2020 at 8:15
  • 12
    \$\begingroup\$ "Your enemy knows a lot about cryptography" Evidently not if this simple code is enough to confound them. ;-p \$\endgroup\$ Jun 3, 2020 at 17:39

30 Answers 30

7
\$\begingroup\$

Charcoal, 9 bytes

⭆S§γ⁺κ⌕γι

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

 S          Input string
⭆           Map over characters and join
        ι   Current character
      ⌕     Find position in
       γ    Printable ASCII
    ⁺       Plus
     κ      Current index
  §         Cyclically indexed into
   γ        Printable ASCII
            Implicitly print
\$\endgroup\$
0
7
\$\begingroup\$

Python 3.8, 55 54 bytes

Output is a list of characters.

lambda s,d=33:[chr((ord(c)-(d:=d-1))%95+32)for c in s]

Try it online!

\$\endgroup\$
5
\$\begingroup\$

J, 22 bytes

(95|<:+#\)&.(_32+3&u:)

Try it online!

Almost word-to-word translation of my dzaima/APL solution.

How it works

(95|<:+#\)&.(_32+3&u:)  NB. Input: string S
(   X    )&.(   Y    )  NB. The "Under" operator; do Y, do X and undo Y
             _32+3&u:   NB. Convert chars to codepoints and subtract 32
       #\               NB. One-based index
    <:+                 NB. Add to the codepoints minus 1
 95|                    NB. Modulo 95
             _32+3&u:   NB. Undo this: add 32 and convert to chars
\$\endgroup\$
5
\$\begingroup\$

R, 56 57 bytes

function(s)intToUtf8((utf8ToInt(s)-33+1:nchar(s))%%95+32)

Try it online!

Edit: thanks to Giuseppe for bug-spotting!

I am beginning to despise string manipulations in R, and the intToUtf8() / utf8ToInt() function names in particular...

\$\endgroup\$
5
  • 1
    \$\begingroup\$ this will fail for a single character input "a" for instance. You can save it at a cost of 2 bytes -- wasn't sure if that would actually work until I tried it! \$\endgroup\$
    – Giuseppe
    Jun 3, 2020 at 18:03
  • 2
    \$\begingroup\$ and yes, R's string manipulation is abysmal. \$\endgroup\$
    – Giuseppe
    Jun 3, 2020 at 18:03
  • 1
    \$\begingroup\$ There's also a 57 byte solution if you get rid of seq \$\endgroup\$
    – Giuseppe
    Jun 3, 2020 at 18:07
  • \$\begingroup\$ Aargh! Well-spotted for the single-character-input bug, and thanks for the suggestion (that's a nice 4-byte shortcut for seq_along that I didn't know about!). \$\endgroup\$ Jun 3, 2020 at 19:11
  • \$\begingroup\$ The 57-byte solution is what I originally had... I was smugly thinking how cleverly I'd saved a byte until you pointed-out that it didn't actually work! \$\endgroup\$ Jun 3, 2020 at 19:12
4
\$\begingroup\$

APL (dzaima/APL), 21 bytes

{95|⍵+⍳≢⍵}⍢(¯32+⎕UCS)

Try it online!

Just trying out the experimental Under operator because the task is screaming for it.

How it works

{95|⍵+⍳≢⍵}⍢(¯32+⎕UCS)  ⍝ Input: string S
          ⍢(¯32+⎕UCS)  ⍝ Convert S to Unicode codepoints and subtract 32
{   ⍵+⍳≢⍵}             ⍝ Add the index to each char
 95|                   ⍝ Modulo 95
          ⍢(¯32+⎕UCS)  ⍝ Undo the operation:
                       ⍝ Add 32 and convert back to Unicode chars
\$\endgroup\$
1
  • 4
    \$\begingroup\$ That operator looks like it is really happy to be used \$\endgroup\$ Jun 3, 2020 at 8:56
4
\$\begingroup\$

05AB1E, 16 10 9 bytes

žQDIkā<+è

-1 byte by porting @Neil's Charcoal answer, so make sure to upvote him!

I/O as a list of characters.

Try it online or verify all test cases.

Explanation:

žQ          # Push the printible ASCII string builtin
  D         # Duplicate it
   I        # Push the input-list of characters
    k       # Get each index in the ASCII string
     ā      # Push the list [1, length] (without popping)
      <     # Decrease it by 1 to make it a 0-based range [0, length)
       +    # Add the values at the same positions in the lists together
        è   # Index each into the ASCII string (0-based and with automatic wraparound)
            # (after which the resulting list of characters is output implicitly)
\$\endgroup\$
4
\$\begingroup\$

Perl 5 (-p), 32 bytes

-1 byte thanks to @Abigail

s/./chr 32+($x++-32+ord$&)%95/ge

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ from the test case it seems it's 94 \$\endgroup\$ Jun 3, 2020 at 9:07
  • 1
    \$\begingroup\$ you're correct the post was updated 41 min ago \$\endgroup\$ Jun 3, 2020 at 9:18
  • 2
    \$\begingroup\$ Save a byte by using ($x++-32+ord$&) instead of (-32+$x+++ord$&). \$\endgroup\$
    – Abigail
    Jun 3, 2020 at 9:51
  • 1
    \$\begingroup\$ @Abigail +++ looks cooler though! \$\endgroup\$
    – Neil
    Jun 3, 2020 at 18:49
4
\$\begingroup\$

C (gcc), 48 47 46 bytes

Saved a byte thanks to 640KB!!!

Saved a byte thanks to dingledooper!!!

i;f(char*s){for(i=32;*s;)*s++=(*s-i--)%95+32;}

Try it online!

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

APL (Dyalog Unicode), 28 24 22 bytes SBCS

-4 bytes thanks to @Adám; -2 bytes thanks to @ngn;

Monadic function expecting a string:

⎕ucs 32+95|⎕ucs-32-⍳∘≢

Try it online! Assumes ⎕IO←0. How the 24-byter works:

32(⎕ucs⊣+95|⊢--∘⍳∘≢)⎕ucs  ⍝ monadic function taking a character vector on the right
                     ⎕ucs  ⍝ convert to unicode code points
32(                 )      ⍝ and then evaluate the expression with 32 as left argument and the code points as right arg
               -∘⍳∘≢        ⍝ do 32 minus (-) the range 0 1 ... [length of input string] (⍳∘≢)
            ⊢-            ⍝ take the right argument (⊢) [the codepoints] and subtract the previous calculation from those [effectively adding the positions and subtracting 32]
         95|               ⍝ and take those numbers modulu 95.
      ⊣+                  ⍝ Take the left (⊣) argument [32] and add it to the numbers we just did mod 95
  ⎕ucs                     ⍝ and convert the new code points to characters.

@Bubbler has a similar answer but making use of an operator that hasn't been implemented in Dyalog APL yet, check it out.

\$\endgroup\$
3
  • \$\begingroup\$ Yours actually consumes 42 bytes (UTF-8) \$\endgroup\$ Jun 3, 2020 at 8:28
  • \$\begingroup\$ @JohnBrookfields I know, I'm on my mobile so I took a bit longer to link the appropriate encoding. C.f. my edited answer \$\endgroup\$
    – RGS
    Jun 3, 2020 at 8:30
  • 1
    \$\begingroup\$ 24: 32(⎕ucs⊣+95|⊢--∘⍳∘≢)⎕ucs \$\endgroup\$
    – Adám
    Jun 3, 2020 at 8:37
3
\$\begingroup\$

Retina 0.8.2, 19 bytes

{*\M!`^.
^.

T`~p`p

Try it online! Link includes test cases. Explanation:

{

Repeat until the input is empty.

*\M!`^.

Output the first character.

^.

Remove the first character.

T`~p`p

Cyclically increment the remaining characters.

12 bytes in Retina 1:

1,Tv`~p`p`.+

Try it online! Link includes test cases. Explanation:

v`.+

Create overlapping matches that start at each character and end at the end of the string.

1,

Ignore the match of the whole input.

T`~p`p`

Cyclically shift each character according to the number of times it was matched.

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

K (oK), 19 bytes

{`c$32+95!x-32-!#x}

Try it online!

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

Java (OpenJDK 8), 80 60 bytes

s->{int i=0;for(char n:s)s[i]+=i-(n+i++>126?95:0);return s;}

Try it online!

Thanks for help from Kevin Cruijssen

\$\endgroup\$
7
  • \$\begingroup\$ Looks like this gives the wrong output for "The random guy" (should be Tig#vftkwv*r"') -- I think your 94 should be 95, that seems to fix it \$\endgroup\$ Jun 4, 2020 at 3:46
  • 1
    \$\begingroup\$ -1 by combining the s[i]+= with the s[i]-= like this: s[i]+=i-(i+s[i++]>126?95:0). Also, you can use a lambda in Java 8+, so the char[]a(char[]s) can be s->: try it online - 64 bytes \$\endgroup\$ Jun 4, 2020 at 14:43
  • 1
    \$\begingroup\$ If you've never used lambdas before in Java, here are some brief examples of different lambdas and how to use it in codegolf. In general, you almost always want to use lambdas in Java codegolfing, unless you need a recursive function, which would still be a regular method. \$\endgroup\$ Jun 4, 2020 at 14:46
  • 1
    \$\begingroup\$ 48 bytes \$\endgroup\$ Jun 5, 2020 at 13:27
  • 1
    \$\begingroup\$ The input object is the output object. It happens all the time in programming. Not every function is pure. And yes, we accept it as is here on Code Golf. \$\endgroup\$ Jun 5, 2020 at 13:35
3
\$\begingroup\$

Haskell, 55 bytes

g '~'=' '
g c=succ c
i!c=iterate g c!!i
zipWith(!)[0..]

Try it online!

The TIO link has f=, but that doesn't contribute to the byte count because I defined it point-free. It's mostly there so that the code compiles.

I didn't play around much with using a point-free definition instead of !, so that might be a place where some bytes can be shaved off. I just figured that the parens and dots would add up. I also want to get rid of the call to iterate, but I'm not sure how. I want something like mtimes...

Haskell + -XParallelListComp, 55 bytes

g '~'=' '
g c=succ c
f s=[iterate g c!!i|c<-s|i<-[0..]]

Try it online!

Unfortunately this isn't any shorter, but I thought it was a cool usage of a pragma. It seems most of the time that pragmas aren't too helpful in golfing.

Explanation

-- g gives the successor of each character according to the specification
g :: Char -> Char
-- the successor of '~' is ' ' (wrap around)
g '~'=' '
-- all other characters have their normal successor
g c=succ c

-- (!) is an infix function that enciphers a character, given an int
(!) :: Int -> Char -> Char
-- iterate produces an infinite list of 'g' applied to 'c' repeatedly,
-- and '!!' indexes into that list at index 'i'. This has the effect
-- of applying 'g' to 'c' 'i' times.
i!c=iterate g c!!i

-- Point-free definition that applies '!' to each character of the input along with its index
zipWith(!)[0..]
\$\endgroup\$
1
  • \$\begingroup\$ Nice idea to try a pragma to substitute zipWith. I did find two shorter approaches, though using ParallelListComp results in the same byte count for those as well. \$\endgroup\$
    – Laikoni
    Jun 17, 2020 at 16:00
3
\$\begingroup\$

x86-16 machine code, IBM PC DOS, 17 16 bytes

Binary:

Build STL.COM using xxd -r:

00000000: b120 b408 cd21 2ac1 d45f 0420 cd29 e0f2  . ...!*.._. .)..

Listing:

B1 20       MOV  CL, 32         ; set up offset / position counter 
        CLOOP: 
B4 08       MOV  AH, 8          ; use DOS API get char from STDIN function 
CD 21       INT  21H            ; read input char into AL 
2A C1       SUB  AL, CL         ; subtract offset 
D4 5F       AAM  95             ; AL = AL % 95 
04 20       ADD  AL, 32         ; restore ASCII offset 
CD 29       INT  29H            ; output AL to console 
E0 F2       LOOPNZ CLOOP        ; keep looping until break, decrement CL

Standalone IBM PC DOS executable program. Input via STDIN, output to console.

Runtime:

enter image description here

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

Haskell, 52 51 bytes

zipWith(%)[-32..]
n%c=toEnum$32+mod(fromEnum c+n)95

Try it online!


Haskell, 52 bytes

zipWith(%)[0..]
n%c=snd(span(<c)$cycle[' '..'~'])!!n

Try it online!

Explanation

  • zipWith(%)[0..] calls the infix function % on each character of the input string along with its index.
  • cycle[' '..'~'] builds a list where the ASCII chars are repeated infinitely.
  • span(<c) partitions this list into a prefix of chars smaller than the current character c and a remainder.
  • snd drops the prefix, so only the list of ASCII chars starting with c remains.
  • !!n returns the nth element from that list.
\$\endgroup\$
2
\$\begingroup\$

Ruby, 42 bytes

->s{i=33;s.map{((_1.ord-i-=1)%95+32).chr}}

Takes input and returns output as an array of characters.

44 byte version because TIO doesn't support ruby 2.7's _1 syntax

->s{i=33;s.map{|c|((c.ord-i-=1)%95+32).chr}}

Try it online!

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

APL (Dyalog Extended), 22 bytes (SBCS)

Full program. Requires ⎕IO←0

⍞(⊢⊇⍨95|⍳⍨+~⍋⊣)' '…'~'

Try it online!

' '…'~' the printable ASCII range

⍞() with the input as left argument, apply the following tacit function to that:

 the left argument (the input)

 the sorting permutation to sort according to the following order:

  ~ the input without any printable ASCII, i.e. an empty string (this means leave all in current positions)

⍳⍨ the indices of the input characters in the printable ASCII

95| division remainder when divided by 95

⊇⍨ use those indices to select from:

   the printable ASCII

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

Befunge-93, 25 bytes

:~:1+!#@_" "-+"_"%" "+,1+

Try it online!

Keeps the character count on the bottom of the stack. Then in a loop, reads a character at the time, subtracts 32 to the character, adds the character count, mods it with 95, adds 32 again, then prints the character. Finally, it adds 1 to the character count.

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

T-SQL, 100 bytes

Added some line changes to make it readable

DECLARE @x INT=0
WHILE @x<len(@)
SELECT
@=substring(@,2,999)+char((ascii(@)+@x-32)%95+32),
@x+=1
PRINT @

Try it online

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

Ly, 25 bytes

ir84*sp[l-l,sp(95)%84*+o]

Try it online!

\$\endgroup\$
1
\$\begingroup\$

JavaScript, 77 bytes

Iterative!

s=>[...s].map((c,i)=>String.fromCharCode((c.charCodeAt()+i-32)%95+32)).join``

Try it online!


JavaScript, 77 bytes

Outer recursive!

f=(s,i=0)=>s[i]?String.fromCharCode((s.charCodeAt(i)+i-32)%95+32)+f(s,i+1):''

Try it online!


JavaScript, 77 bytes

Inner recursive!

s=>(F=i=>s[i]?String.fromCharCode((s.charCodeAt(i)+i-32)%95+32)+F(i+1):'')(0)

Try it online!

\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 42 bytes

s=>Buffer(s).map((c,i)=>(c+i-32)%95+32)+''

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Jelly, 9 bytes

O+J_32ịØṖ

Try it online!

How?

O+J_32ịØṖ - Link: list of characters, S  - e.g. "zzzzzzzzz"
O         - cast (S) to ordinal values          [122,122,122,122,122,122,122,122,122]
  J       - range of length (S)                 [  1,  2,  3,  4,  5,  6,  7,  8,  9]
 +        - add (vectorises)                    [123,124,125,126,127,128,129,130,131]
    32    - thirty-two                          32
   _      - subtract                            [ 91, 92, 93, 94, 95, 96, 97, 98, 99]
       ØṖ - printable ASCII characters          " !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
      ị   - index into (1-based & modular)      "z{|}~ !"#"
\$\endgroup\$
1
\$\begingroup\$

C (gcc), 46 50 48 bytes

x;f(char*s){*s?*s=(*s+x++-32)%95+32,x=f(s+1):0;}

Try it online!

\$\endgroup\$
6
  • \$\begingroup\$ This isn't legal, you can't depend on a variable being externally set outside of the function. \$\endgroup\$
    – Noodle9
    Jun 3, 2020 at 11:13
  • \$\begingroup\$ I don't depend on it for the 1st iteration, only subsequent ones... (proof), is that also illegal? \$\endgroup\$ Jun 3, 2020 at 11:41
  • 2
    \$\begingroup\$ No, it is not legal. It has to work independently for any run, not just the first. In C this means, for instance, a function can't depend on global variables being set to 0 at the start of a program run. \$\endgroup\$
    – Noodle9
    Jun 3, 2020 at 12:12
  • \$\begingroup\$ Can I say the function takes an extra argument that must always be 0 or is that illegal too? \$\endgroup\$ Jun 3, 2020 at 12:24
  • \$\begingroup\$ @Noodle9 Do you have a link to the rules thread? I can't seem to find it. \$\endgroup\$ Jun 3, 2020 at 12:35
1
\$\begingroup\$

Erlang (escript), 68 bytes

f(I,[H|T])->[(H+I-32)rem 95+32]++f(I+1,T);f(_,[])->[].
f(I)->f(0,I).

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Stax, 9 bytes

éñÇöo♣j0♦

Run and debug it

\$\endgroup\$
1
\$\begingroup\$

Wolfram Language, 68 bytes

FromCharacterCode@Mod[#&~Array~Length@#+#-1,95,32]&@*ToCharacterCode

Uses a little function composition to make it shorter, but other than that, does pretty much what it says on the tin. Converts the string to character codes, adds the proper offset to each, wraps around if needed using Mod (there's an optional argument for offset that I use here), then converts back to a string.

\$\endgroup\$
1
  • \$\begingroup\$ I believe #&~Array~Length@# can be replaced with Range@Length@# for a 3-byte improvement. \$\endgroup\$ Jun 6, 2020 at 4:34
0
\$\begingroup\$

Keg, 19 bytes

0&(⑻+:\~>[\~% +;],⑹

Try it online!

Woooo! I managed to beat APL somehow! This is a very literal interpretation of the challenge, except for the part where overflow values are decremented after modulusing.

\$\endgroup\$
0
\$\begingroup\$

Befunge-98 (FBBI), 20 bytes

'!v
-1<,+ '%_'-\~@#:

Try it online!

Initially pushes d=33 on the stack. On every iteration, d is reduced by one and (input-d)%95+32 is printed.

\$\endgroup\$
0
\$\begingroup\$

Pyth, 29 bytes

VlQ=+Y-C@QN32;s.eC+%+bk95 32Y

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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