14
\$\begingroup\$

We'll define the ASCII Odd/Even Cipher via the below pseudocode:

Define 'neighbor' as the characters adjacent to the current letter in the string

If the one of the neighbors is out of bounds of the string, treat it as \0 or null

Take an input string

For each letter in the string, do
  If the 0-based index of the current letter is even, then
    Use the binary-or of the ASCII codes of both its neighbors
  Else
    If the ASCII code of the current letter is odd, then
      Use the binary-or of itself plus the left neighbor
    Else
      Use the binary-or of itself plus the right neighbor
  In all cases,
    Convert the result back to ASCII and return it
  If this would result in a code point 127 or greater to be converted, then
    Instead return a space

Join the results of the For loop back into one string and output it

For example, for input Hello, the output is emmol, since

  • The H turns to \0 | 'e' which is e
  • The e turns to 'e' | 'l', or 101 | 108, which is 109 or m
  • The first l also turns to 101 | 108 or m
  • The second l turns to 108 | 111, which is 111 or o
  • The o turns to 108 | \0, or l

Input

  • A sentence composed solely of printable ASCII characters, in any suitable format.
  • The sentence may have periods, spaces, and other punctuation, but will only ever be one line.
  • The sentence will be at least three characters in length.

Output

  • The resulting cipher, based on the rules described above, returned as a string or output.

The Rules

  • Either a full program or a function are acceptable.
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.

Examples

Input on one line, output on the following. Blank lines separate examples.

Hello
emmol

Hello, World!
emmol, ww~ved

PPCG
PSWG

Programming Puzzles and Code Golf
r wogsmmoonpuu ~ meannncoooeggonl

abcdefghijklmnopqrstuvwxyz
bcfefgnijknmno~qrsvuvw~yzz

!abcdefghijklmnopqrstuvwxyz
aaccgeggoikkomoo qsswuww yy

Test 123 with odd characters. R@*SKA0z8d862
euutu133www|todddchizsscguwssr`jS{SK{z~|v66
\$\endgroup\$
5
  • 3
    \$\begingroup\$ Is this really a cipher? Doesn't seem to be a way to decipher it. \$\endgroup\$
    – pipe
    Sep 24, 2016 at 10:41
  • \$\begingroup\$ Given that the o changes to l in the first example, I'm pretty sure your specs ensure that the first o does not change to l in the second example. It should change to 'l' | ',', whatever that is, right? \$\endgroup\$ Sep 24, 2016 at 18:07
  • \$\begingroup\$ @pipe Yeah. Not really a "cipher" but, not really sure what to call it. It's not really a hash, either. Of the tags we've got, "cipher" seemed closest, so that's what I went with. \$\endgroup\$ Sep 26, 2016 at 12:24
  • \$\begingroup\$ @GregMartin Yes, it goes to 'l' | ',', which is 108 | 44 --> 1101111 | 0101100, which becomes 108, which is l. The , happens to line up with the l, so there's no change when the binary-or takes place. \$\endgroup\$ Sep 26, 2016 at 12:29
  • \$\begingroup\$ Oh, it's really the binary-OR ... I was thinking the binary-XOR. Thank you for the clarification. On the other hand, this speaks even more to pipe's observation that this "cipher"can't really be deciphered, as far as I can tell. \$\endgroup\$ Sep 26, 2016 at 18:14

10 Answers 10

4
\$\begingroup\$

Perl, 63 62 bytes

Includes +4 for -lp

Give input on STDIN

oddeven.pl:

#!/usr/bin/perl -lp
s%.%(--$|?$n|$':$&|(ord$&&1?$n:$'))&($n=$&,~v0)%eg;y;\x7f-\xff; ;

This works as shown, but to get the claimed score this must be put in a file without final ; and newline and the \xhh escapes must be replaced by their literal values. You can do this by putting the code above in the file and running:

perl -0pi -e 's/\\x(..)/chr hex $1/eg;s/;\n$//' oddeven.pl
\$\endgroup\$
3
\$\begingroup\$

Python 2, 138 131 bytes

s="\0%s\0"%input();r=''
for i in range(len(s)-2):L,M,R=map(ord,s[i:i+3]);a=i%2and[R,L][M%2]|M or L|R;r+=chr(a*(a<127)or 32)
print r

Try it online (contains all test cases)

Less golfed:

def f(s):
    s="\0%s\0"%s
    r=''
    for i in range(1,len(s)-1):
        if i%2: # even (parity is changed by adding \x00 to the front)
            a=ord(s[i-1]) | ord(s[i+1])
        else:   # odd
            a=ord(s[i])
            if a%2: # odd
                a|=ord(s[i-1])
            else:   # even
                a|=ord(s[i+1])
        r+=chr(a if a<127 else 32)
    print r

Try it online (ungolfed)

I add \x00 to both sides of the string so that I don't have to worry about that during bitwise or-ing. I loop along the string's original characters, doing bitwise operations and adding them to the result, following the rules for parity.

\$\endgroup\$
4
  • \$\begingroup\$ Dang, I'm jealous of that |= ... equivalent in PowerShell would be $a=$a-bor$b \$\endgroup\$ Sep 23, 2016 at 20:09
  • \$\begingroup\$ @TimmyD I didn't actually end up using it, but yeah. It's nice. If only Python had a?b:c like JS. \$\endgroup\$
    – mbomb007
    Sep 23, 2016 at 20:39
  • \$\begingroup\$ You can replace if a%2: # odd a|=ord(s[i-1]) else: # even a|=ord(s[i+1]) with a|=ord(s[i+1-2*(a%2)]) \$\endgroup\$ Sep 25, 2016 at 17:20
  • \$\begingroup\$ @NoSeatbelts That's my ungolfed code, which will be left as-is for readability purposes. The golfed submission is the top program. \$\endgroup\$
    – mbomb007
    Sep 26, 2016 at 13:44
2
\$\begingroup\$

C - 101 bytes

i,k;f(char*p){for(i=0;*p;++p,++i)putchar((k=i&1?*p&1?*p|p[-1]:*p|p[1]:i?p[-1]|p[1]:p[1])<127?k:' ');}

We don't even have to check if it's the last item in the string because strings in C are null-terminated.

Explanation

Rather simple:

Use &1 to test for odd/evenness and ternary expressions to replace if/elses. Increment the char *p to reduce the number of brackets required.

\$\endgroup\$
1
  • \$\begingroup\$ Nice answer -- welcome to PPCG! \$\endgroup\$ Sep 23, 2016 at 20:35
2
\$\begingroup\$

Mathematica, 152 bytes

FromCharacterCode[BitOr@@Which[OddQ@Max@#2,#~Drop~{2},OddQ@#[[2]],Most@#,True,Rest@#]/._?(#>126&)->32&~MapIndexed~Partition[ToCharacterCode@#,3,1,2,0]]&

Explanation

ToCharacterCode@#

Converts string to ASCII codes

Partition[...,3,1,2,0]

Partitions the ASCII codes to length 3, offset 1 partitions, with padded 0s.

...~MapIndexed~...

Applys a function for each partition.

Which[...]

If...else if... else in Mathematica.

OddQ@Max@#2

Checks whether the index (#2) is odd. (Max is for flattening); since Mathematica index starts at 1, I used OddQ here, not EvenQ

Drop[#,{2}]

Takes the ASCII codes of the left and right neighbors.

OddQ@#[[2]]

Checks whether the ASCII code of the corresponding character is odd.

Most@#

Takes the ASCII codes of the character and the left neighbor.

Rest@#

Takes the ASCII codes of the character and the right neighbor.

BitOr

Applys or-operation.

/._?(#>126&)->32

Replaces all numbers greater than 126 with 32 (space).

FromCharacterCode

Converts ASCII code back to characters and join them.

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to PPCG! Could you add a bit of explanation for people (like me) who aren't well versed in Mathematica? Also be sure to check out Tips for golfing in Mathematica for some suggestions. Enjoy your stay! \$\endgroup\$ Sep 23, 2016 at 19:29
  • 1
    \$\begingroup\$ A few improvements: Accepting and returning a list of characters instead of an actual string object is completely fine and saves a lot on those From/ToCharacterCode functions. Then it looks like your Drop can use infix notation: #~Drop~{2}. And it seems that you're applying BitOr to every possible output of the Which so why not apply it afterwards and only once? \$\endgroup\$ Sep 23, 2016 at 19:32
2
\$\begingroup\$

Ruby 133 128 108 106 bytes

Jordan helped me save 20 bytes and cia_rana helped me save 2 bytes :)

->s{p s[-i=-1]+s.bytes.each_cons(3).map{|x,y,z|i+=1;a=i%2>0?x|z :y%2>0?y|x :y|z;a>126?' ':a.chr}*""+s[-2]}

s is taken as the input string.

Example output with s="Test 123 with odd characters. R@*SKA0z8d862":

"euutu133www|todddchizsscguwssr`jS{SK{z~|v66"

Explanation

The above code is very unreadable so here is an explanation. The code is kind of hacky, I'm quit new to ruby so I bet there is a shorter way to do this :)

b=s[1] # for the first character we always use the right neighbour
       # because `\0 | x` will always return x any way. 0 is the
       # left neighbour and x is the right neigbour
s.bytes.each_cons(3).with_index{|c,i| # oh boy, first we convert the string to ascii with each_byte
                                          # we then traverse the resulting array with three elements at
                                          # a time (so for example if s equals "Hello", c will be equal
                                          # to [72, 101, 108])
  if (i+1) % 2 < 1 # if the middle letter (which is considered our current letter) is even
    a = c[0] | c[2] # we use the result of binary-or of its neighbours
  else
    if c[1] % 2 > 0 # if the code of the current letter is odd
      a = c[1] | c[0] # we use the result of binary-or of itself and its left neighbour
    else
      a = c[1] | c[2] # we use the result of binary-or of itself and its right neighbour
    end
  end
  if a>126
    b<<' ' # if the result we use is greater or equal to 127 we use a space
  else
    b<<a.chr # convert the a ascii value back to a character
  end
}
p b+s[-2] # same as the first comment but now we know that x | \0 will always be x
          # this time x is the last characters left neighbour
\$\endgroup\$
14
  • \$\begingroup\$ I'm fairly certain the output needs to be on one line, since the input is also. \$\endgroup\$
    – mbomb007
    Sep 23, 2016 at 20:06
  • \$\begingroup\$ @mbomb007 bummer, then I have to use print instead of p :p \$\endgroup\$
    – Linus
    Sep 23, 2016 at 20:07
  • \$\begingroup\$ @TimmyD oh, so I can't print it to the output at different times? \$\endgroup\$
    – Linus
    Sep 23, 2016 at 20:09
  • \$\begingroup\$ @TimmyD ok, so the above is allowed? It now prints everything on one line. \$\endgroup\$
    – Linus
    Sep 23, 2016 at 20:18
  • 1
    \$\begingroup\$ You can write as shown below: ->s{p s[-i=-1]+s.bytes.each_cons(3).map{|x,y,z|i+=1;a=i%2>0?x|z :y%2>0?y|x :y|z;a>126?' ':a.chr}*""+s[-2]} \$\endgroup\$
    – cia_rana
    Sep 24, 2016 at 12:56
1
\$\begingroup\$

Jelly, 33 31 bytes

2ịḂị|2\
|ṚḢ,Ç
O0,0jṡ3µÇ€⁸JḂ¤ị"Ọ

A straight-forward approach that can surely be made shorter.

Try it online!

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

J, 42 bytes

4:u:3({.OR{:)`((2|1&{){2:OR/\|.)\0,~0,3&u:

Uses the property that verbs in J can be applied in an alternating fashion using a gerund ` for certain adverbs such as infix \.

Usage

   f =: 4:u:3({.OR{:)`((2|1&{){2:OR/\|.)\0,~0,3&u:
   f 'Hello'
emmol
   f 'Hello, World!'
emmol,ww~ved
   f 'PPCG'
PSWG
   f 'Programming Puzzles and Code Golf'
rwogsmmoonpuu~meannncoooeggonl
   f 'abcdefghijklmnopqrstuvwxyz'
bcfefgnijknmno~qrsvuvw~yzz
   f '!abcdefghijklmnopqrstuvwxyz'
aaccgeggoikkomooqsswuwwyy
   f 'Test 123 with odd characters. R@*SKA0z8d862'
euutu133www|todddchizsscguwssr`jS{SK{z~|v66

Explanation

4:u:3({.OR{:)`((2|1&{){2:OR/\|.)\0,~0,3&u:  Input: string S
                                      3&u:  Convert each char to an ordinal
                                    0,      Prepend 0
                                 0,~        Append 0
    3                           \           For each slice of size 3
     (      )`                                For the first slice (even-index)
          {:                                    Get the tail
      {.                                        Get the head
        OR                                      Bitwise OR the head and tail
             `(                )              For the second slice (odd-index)
                             |.                 Reverse the slice
                       2:   \                   For each pair
                         OR/                      Reduce using bitwise OR
                  1&{                           Get the middle value of the slice
                2|                              Take it modulo 2
                      {                         Index into the bitwise OR pairs and select
                                              Repeat cyclically for the remaining slices
4:u:                                        Convert each ordinal back to a char and return
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 125 118 114 bytes

Embarrassingly long, but charCodeAt and String.fromCharCode alone are 29 bytes. :-/

s=>[...s].map((_,i)=>String.fromCharCode((x=(C=i=>s.charCodeAt(i))((i-1)|1)|C(i+1-2*(C(i)&i&1)))>126?32:x)).join``

How it works

Each character at position i is translated with the following formula, which covers all rules at once:

C((i - 1) | 1) | C(i + 1 - 2 * (C(i) & i & 1))

where C(n) returns the ASCII code of the n-th character of the input string.

Demo

let f =
    
s=>[...s].map((_,i)=>String.fromCharCode((x=(C=i=>s.charCodeAt(i))((i-1)|1)|C(i+1-2*(C(i)&i&1)))>126?32:x)).join``

console.log(f("Hello"));
console.log(f("Hello, World!"));
console.log(f("PPCG"));
console.log(f("Programming Puzzles and Code Golf"));
console.log(f("abcdefghijklmnopqrstuvwxyz"));
console.log(f("!abcdefghijklmnopqrstuvwxyz"));
console.log(f("Test 123 with odd characters. R@*SKA0z8d862"));

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

PHP, 107 97 bytes

probably golfable.

for(;$i<strlen($s=$argv[1]);$i++)echo chr(ord($s[$i-1+$i%2])|ord($s[$i+1-2*($i&ord($s[$i])&1)]));
\$\endgroup\$
1
\$\begingroup\$

C#, 145 bytes

s=>{var r=s[1]+"";int i=1,l=s.Length,c;for(;i<l;i++){c=i>l-2?0:s[i+1];c=i%2<1?s[i-1]|c:s[i]|(s[i]%2>0?s[i-1]:c);r+=c>'~'?' ':(char)c;}return r;};

Full program with ungolfed method and test cases:

using System;

namespace ASCIIOddEvenCipher
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<string,string>f= s=>
            {
                var r = s[1] + "";
                int i = 1, l = s.Length, c;
                for(;i < l; i++)
                {
                    c = i>l-2 ? 0 : s[i+1];
                    c = i%2<1 ? s[i-1]|c : s[i]|(s[i]%2>0 ? s[i-1] : c);
                    r += c > '~' ? ' ' : (char)c;
                }
                return r;
            };
            
            //test cases:
            Console.WriteLine(f("Hello"));  //emmol
            Console.WriteLine(f("Hello, World!"));  //emmol, ww~ved
            Console.WriteLine(f("PPCG"));   //PSWG
            Console.WriteLine(f("Programming Puzzles and Code Golf"));  //r wogsmmoonpuu ~ meannncoooeggonl
            Console.WriteLine(f("abcdefghijklmnopqrstuvwxyz")); //bcfefgnijknmno~qrsvuvw~yzz
            Console.WriteLine(f("!abcdefghijklmnopqrstuvwxyz"));    //aaccgeggoikkomoo qsswuww yy
            Console.WriteLine(f("Test 123 with odd characters. R@*SKA0z8d862"));    //euutu133www|todddchizsscguwssr`jS{SK{z~|v66
        }
    }
}

This turned out to be longer than I thought...

\$\endgroup\$

Your Answer

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

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