18
\$\begingroup\$

Characters in strings are sometimes represented as their ASCII hexadecimal codes. Printable characters have two hex digits in their representation. Swapping those digits leads to another character, which will be our output.

The table of relevant character codes can be found on Wikipedia.

Details

  1. Take a string as input.
  2. For each character:
    1. Find corresponding hex value of ASCII code.
    2. Swap (reverse) the hex digits.
    3. Convert back to character.
  3. Output the new string.

Rules

  • To make thinks easier, let's consider only characters "reversible" within standard printable ASCII range - that is codepoints (in hex): 22-27,32-37,42-47,52-57,62-67,72-77 or characters: "#$%&'234567BCDEFGRSTUVWbcdefgrstuvw.
  • Input and output should be strings or closest equivalent in your language of choice.
  • You may assume the input is non-empty.
  • This is , the usual rules apply.

Example

input: debug
hex codes: 64 65 62 75 67
reversed: 46 56 26 57 76
output: FV&Wv

Test cases

bcd <-> &6F
234 <-> #3C
screw <-> 76'Vw
debug <-> FV&Wv
BEEF <-> $TTd
7W66V77 <-> success
"#$%&'234567BCDEFGRSTUVWbcdefgrstuvw <-> "2BRbr#3CScs$4DTdt%5EUeu&6FVfv'7GWgw
\$\endgroup\$
1
  • 2
    \$\begingroup\$ So... rotate by 4 on 8-bit ASCII character values. Converting to hex and back is merely one implementation, but seems to me an over-complicated way to describe it. (But I guess describes in terms of things more languages can do easily; some can't easily manipulate characters as integers.) \$\endgroup\$ May 26 at 0:48

29 Answers 29

13
\$\begingroup\$

Vyxal s, 5 bytes

CHRHC

Try it Online!

Look ma, no unicode, and it's a palindrome!

C   C # To charcodes...
 H H  # To hexadecimal versions of charcodes
  R   # Reverse each
      # (s flag transforms output into string)
\$\endgroup\$
5
  • \$\begingroup\$ You need to add a trailing join, since I/O as strings is mandatory in this challenge and overwrites the default I/O rules (I read past it initially with my answers as well). Unfortunately it breaks the palindrome. :( (Unless you add a no-op leading join as well, haha.) \$\endgroup\$ May 25 at 8:39
  • 2
    \$\begingroup\$ @KevinCruijssen Fortunately, the s flag exists :) \$\endgroup\$
    – emanresu A
    May 25 at 9:44
  • \$\begingroup\$ Ah indeed. Nice. :D \$\endgroup\$ May 25 at 10:29
  • \$\begingroup\$ Don't you have to count the flag? Wouldn't that make it 6 bytes? \$\endgroup\$ May 25 at 21:43
  • 3
    \$\begingroup\$ @DavidConrad Since late 2017 not anymore. Flags just count as a different languages now. \$\endgroup\$ May 25 at 21:51
9
\$\begingroup\$

Ruby, 26 bytes

Accepts input from STDIN.

$<.bytes{|c|putc c*257>>4}

Try it online!

Alternative 26s:

$<.bytes{|c|putc c*16%255}
$<.bytes{|c|putc c*16.065} # was discovered by @Arnauld

Ruby, 28 bytes

->s{s.unpack('H*').pack'h*'}

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ If you don't mind Perl in your Ruby, you can save one byte on your second answer with a full program + -p: Try it online! \$\endgroup\$ May 25 at 14:30
  • \$\begingroup\$ @DomHastings Yes, that works indeed. I usually don't use flags since I see them as a "cheap" way to save bytes. Especially because if we include the -p in the count, it becomes one longer! \$\endgroup\$ May 25 at 22:58
8
\$\begingroup\$

Python 3, 55 54 bytes

lambda x:''.join(chr(c%16*16|c>>4)for c in map(ord,x))

Try it online!

Can be shorter if bytestrings are permitted:

Python 3, 38 bytes

lambda x:bytes(c%16*16|c>>4for c in x)

Try it online!

\$\endgroup\$
2
  • 3
    \$\begingroup\$ 38 bytes if you use byte strings: Try it online! \$\endgroup\$
    – pxeger
    May 25 at 7:16
  • \$\begingroup\$ @pxeger I suppose "strings or closest equivalent" does admit an inclusive reading lmao \$\endgroup\$ May 25 at 7:21
6
\$\begingroup\$

APL (dzaima/APL), 14 13 bytes

−1 thanks to Unrelated String

Anonymous tacit prefix function.

⊖⍢(0 16⊤⎕UCS)

Try it online!

 flip…

⍢() while argument is represented as…

0 16⊤… two-digit hexadecimal representation of…

  ⎕UCS Universal Character Set code points

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

JavaScript (Node.js),  33  32 bytes

Saved 1 byte thanks to @dingledooper

s=>Buffer(s).map(n=>n*257>>4)+''

Try it online!

How?

When map'ing over a Buffer, the updated values are implicitly truncated to bytes, so we can just do n * 257 >> 4 without worrying about the upper nibble.

\$\endgroup\$
3
  • 3
    \$\begingroup\$ n*257>>4 saves one byte. \$\endgroup\$ May 25 at 7:31
  • 2
    \$\begingroup\$ @dingledooper Nice! FWIW, both n*16.062 and n*16.063 also work for the character set defined in the challenge. \$\endgroup\$
    – Arnauld
    May 25 at 8:40
  • 1
    \$\begingroup\$ That's hilarious, making use of the implicit rounding! I've also found n/.06225 and n*16%255. The latter might be useful in other languages as it already truncates the upper bits. \$\endgroup\$ May 25 at 23:06
5
\$\begingroup\$

Perl 5 + -p, 22 bytes

A port of @dingledooper's second Ruby answer.

$_=pack'h*',unpack'H*'

Try it online!

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

sh + coreutils, 34 bytes

xxd -p|tr -d \\n|rev|xxd -r -p|rev

Try it online!

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

Husk, 11 bytes

m(cB16↔B16c

Try it online!

map ( characters of Base-16 values of =reverse of Base-16 representation of character codes of the input.

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

Retina 0.8.2, 33 bytes

T`#-'4-7E-GVWgvuet\dTscSCrbRB2`Ro

Try it online! Link includes test cases. Explanation: o substitutes for the transliteration string and R reverses it so each listed character gets mapped to the one opposite. Character ranges are used twice to reduce the byte count and once to avoid quoting the E but as d maps to F I can't use character ranges for both d and E.

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

GeoGebra, 107 bytes

s=""
InputBox(s)
UnicodeToText(Zip(FromBase(Sum(Reverse(Split(ToBase(a,16),{""}))),16),a,TextToUnicode(s)))

To enter this code into GeoGebra, copy/paste in one line at a time and press enter to go to a new line. Do not copy paste directly; it won't work.

Input goes in the Input Box. It was a bit of a challenge to get this working, as GeoGebra has a pretty limited set of tools for working with strings.

Try It On GeoGebra!

Explanation

Zip(...,a,TextToUnicode(s)): For every integer element a in the list of char codes of each character in the input string s:

FromBase(Sum(Reverse(Split(ToBase(a,16),{""}))),16)
                           ToBase(a,16)                Convert a to a base 16 string
                     Split(            ,{""})          Split the string into a list of characters
             Reverse(                        )         Reverse the list
         Sum(                                 )        Concatenate the characters into a string
FromBase(                                      ,16)    Convert the base 16 string back to base 10 

UnicodeToText(...): Convert the list of char codes back to a string

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

JavaScript (ES6), 113 107 77 bytes

s=>String.fromCharCode(...[...s].map(c=>c.charCodeAt()).map(c=>c%16*16|c>>4))

77 bytes if we use Unrelated String's method.

Try it online!


107: Slightly better.

s=>String.fromCharCode(...[...s].map(c=>parseInt([...c.charCodeAt().toString(16)].reverse().join(''), 16)))

Try it online!


Original 113 byte answer:

s=>[...s].map(c=>String.fromCharCode(parseInt([...c.charCodeAt().toString(16)].reverse().join(''), 16))).join('')

Try it online!

This is my first time ever posting here. Obviously not the best solution but I wanted to compete.

\$\endgroup\$
1
  • \$\begingroup\$ Welcome and nice first answer! \$\endgroup\$
    – pajonk
    May 26 at 7:15
3
\$\begingroup\$

Jelly, 7 bytes

⁴ɓObUḅỌ

Try it online!

Not a palindrome, but still sounds kind of goofy.

  O        Character codes.
   b       Convert to base
⁴ɓ         16,
    U      reverse each,
⁴ɓ   ḅ     convert from base 16,
      Ọ    and convert from character codes.
\$\endgroup\$
3
\$\begingroup\$

05AB1E, 6 bytes

ÇhíHçJ

Try it online or verify all test cases.

Explanation:

Ç      # Convert the (implicit) input-string to a list of codepoint integers
 h     # Convert each integer to a hexadecimal string
  í    # Reverse each string in the list
   H   # Convert each string from hexadecimal to a base-10 integer
    ç  # Convert each codepoint-integer to a character
     J # Join them together to a string, since a string I/O that overwrites the
       # default I/O ruling is mandatory for this challenge
       # (after which it is output implicitly as result)
\$\endgroup\$
3
\$\begingroup\$

MathGolf, 6 bytes

Æ$¢x¢$

I guess it could be a palindrome by adding a trailing no-op Æ. ;)

Try it online.

Explanation:

Æ       # Loop over the characters of the (implicit) input-string,
        # using five characters as inner code-block:
 $      #  Convert the character to a codepoint-integer
  ¢     #  Convert the integer to a hexadecimal string
   x    #  Reverse it
    ¢   #  Convert it from a hexadecimal string to an integer
     $  #  Convert it from a codepoint-integer to a character
        # (after the loop, implicitly output the entire stack joined together)
\$\endgroup\$
3
\$\begingroup\$

x86 32-bit machine code, 7 bytes

C0 02 04 42 E2 FA C3

Try it online!

Following the fastcall calling convention, this takes the length and address of a string in ECX and EDX, respectively, and modifies the string in place.

In assembly:

f:  rol BYTE PTR [edx], 4   # Rotate the character left by 4 bits, swapping its nybbles.
    inc edx     # Add 1 to EDX, advancing the pointer.
    loop f      # Subtract 1 from ECX and jump back if it is nonzero.
    ret         # Return.
\$\endgroup\$
2
  • \$\begingroup\$ Nice, similar to what I was thinking. (And neat idea to use inline asm in C for the test caller, with a hex dumper). I was thinking we could handle 0-terminated C strings by branching on FLAGS from rol (maybe with scasb / rol byte [rdi-1], 4 / jnz) for only one extra byte, or break even in 64-bit mode. But no, rol leaves ZF unmodified so that would just search for a match for whatever garbage is in AL. Explicit-length strings with the loop instruction seem to be the way to go. Implicit-length could xor-zero EAX ahead of a loop that used rol/scasb/jnz, so 2 extra bytes. \$\endgroup\$ May 26 at 0:29
  • \$\begingroup\$ An x86-64 SysV version of your version would be the same size, using scasb to as a 1-byte inc rdi, with args in RDI and RCX. (C prototype nibbleswap(char *p, int dummy, int dummy, uint64_t length), if you want to describe it to a C compiler.) \$\endgroup\$ May 26 at 0:32
3
\$\begingroup\$

C (clang), 36 \$\cdots\$ 34 29 bytes

f(*s){for(;*s++=*s*16%255;);}

Try it online!

Saved a byte thanks to ovs!!!
Saved a byte thanks to Juan Ignacio Díaz!!!
Saved 5 bytes thanks to dingledooper!!!

Inputs a pointer to a wide string.
Returns the reverse hex cipher through the input pointer.

\$\endgroup\$
4
  • \$\begingroup\$ you can save a byte with recursion: f(*s){(*s=*s>>4|*s%16<<4)&&f(++s);} \$\endgroup\$
    – ovs
    May 25 at 14:30
  • 2
    \$\begingroup\$ not sure how this works 34 bytes \$\endgroup\$ May 25 at 16:45
  • 1
    \$\begingroup\$ @JuanIgnacioDíaz Very nice -thanks! :D The increment s++ happens after all the calculations have been made and the result has been stored back in *s. At the end of the string that calculation will be \$0\$ so the for loop ends. \$\endgroup\$
    – Noodle9
    May 25 at 18:03
  • \$\begingroup\$ A shorter formula: *s*16%255 \$\endgroup\$ May 26 at 4:07
3
\$\begingroup\$

Rust, 49 bytes

fn f(s:&mut[u8]){for c in s{*c=c.rotate_left(4)}}

Rust doesn't have a string type for plain-ASCII strings, only UTF-8 with enforcement. An SO answer recommends &[u8] slices for working with ASCII bytes. I realize this is bending the rules, so I did also find sufficient incantations to get Rust to let me do this to the bytes of an actual str primitive type (a slice of which is like a C char* + length). IDK if there's any shorter way to write this, or a way to use compiler options instead of the unsafe{}.

Rust using str strings, 72 bytes

fn h(s:&mut str){unsafe{for c in s.as_bytes_mut(){*c=c.rotate_left(4)}}}

These could apparently be smaller as closures, but I'm just taking baby steps towards learning some Rust. Suggestions welcome.

Expanding ASCII codes to hex-strings and then swapping pairs is equivalent to rotating the original integer by 4, to swap nibbles. Rust is fun for that because it has as language built-in most of the common things many CPU instructions can do to integers. Instead of needing voodoo that a compiler has pattern-recognize back to a rotate or popcount for portable code to run efficiently. The u8 docs include all these operations.

These compile to x86-64 asm that rotates the bytes in an array, as you can see on Godbolt. (Note the rol byte ptr [rcx], 4 in the cleanup loop if the SIMD code isn't easy to follow. (2x shifts and vpternlogd as a bit-blend.) Unfortunately the -C opt-level=1 asm is rather hard to follow, and the opt-level=2 code vectorizes, so I couldn't get just a simple scalar loop to look at more easily. But I'm just using range-for stuff so I don't have to worry about loop bounds.)

Unlike many languages, Rust does not do implicit promotion to wider types for operators like *, or even implicit conversion of integer types on assignment. The *16%255 hack does not save space over .rotate_left(4). I don't know if both sets of () are truly necessary around the as i32 and so on, but I'm pretty sure the as something and as u8 are necessary.

fn g(s:&mut[u8]){for c in s{*c=((*c as i32)*16%255) as u8}}
\$\endgroup\$
3
\$\begingroup\$

Java 8, 116 61 57 bytes

s->s.chars().forEach(c->System.out.printf("%c",c*16%255))

-55 bytes by porting @UnrelatedString's Python answer, so make sure to upvote him/her as well!
-4 bytes thanks to @dingledooper

Input as String (which is mandatory according to the rules and overwrites the default rules..), but outputs directly to STDOUT.

Try it online.

Explanation:

s->                    // Method with String parameter and no return-type
  s.chars().forEach(c->//  Loop over the input-characters as integers:
    System.out.printf( //   Print:
     "%c",             //    Converting a codepoint-integer to character:
     c*16              //     The integer multiplied by 16
         %255))        //     Modulo-255

With default I/O rules this could have been 21 bytes, by having the I/O as a stream of codepoint integers: s->s.map(c->c*16%255) - Try it online.

Original 116 bytes answer:

s->s.chars().forEach(c->{var t="".format("%02x",c).split("");System.out.print((char)Long.parseLong(t[1]+t[0],16));})

Again input as a String and output directly to STDOUT.

Try it online.

Explanation:

s->                     // Method with String parameter and no return-type
  s.chars().forEach(c->{//  Loop over the input-characters as integers:
    var t="".format("%02x",c)
                        //   Convert the integer to a hexadecimal string of two hex-digits
          .split("");   //   Convert it to a String-array
    System.out.print(   //   Print:
      (char)            //    Cast a long to character:
        Long.parseLong( //     Convert a string to a long:
          t[1]+t[0],    //      The hex-digits in reversed order
          16));})       //      Converted from base-16 to base-10

Some notes:

  • It loops over the array as integers with for(int c:s) so we can use c in the "".format(...,c), instead of as characters with for(var c:s) so we would need an explicit cast to integer (int)c.
  • Although Long.valueOf is 2 bytes shorter, it'll return an object Long, which requires a cast to long before the cast to char ((char)(long)Long.valueOf(...)). So instead we use Long.parseLong, which already returns a primitive long.
\$\endgroup\$
5
  • \$\begingroup\$ So Long is the shortest named integer type? \$\endgroup\$
    – Neil
    May 25 at 10:30
  • 1
    \$\begingroup\$ @Neil Yep. There is in increasing order of bits: Byte (8 bits); Short (16 bits); Integer (32 bits); and Long (64 bits). Oh, and java.math.BigInteger of course. ;) As primitives int is usually shortest, though. \$\endgroup\$ May 25 at 10:32
  • \$\begingroup\$ System.out.write is probably what you want ;) \$\endgroup\$ May 26 at 4:10
  • \$\begingroup\$ @dingledooper write only writes it to a PrintStream-buffer, but doesn't actually print. Since it's not autoflushable, I would need an additional System.out.flush(); at the end somewhere in order to print the things I put into System.out.write. When printing in Java, you almost always just need System.out.print; .println; .printf, and in very rare cases System.console().printf if the character u is disallowed for restricted-source challenges. \$\endgroup\$ May 26 at 10:57
  • 1
    \$\begingroup\$ @KevinCruijssen Ah of course, my bad. Then may I suggest c*16%255 which shortens your formula a bit. \$\endgroup\$ May 26 at 19:28
3
\$\begingroup\$

Factor, 29 23 bytes

[ [ 4 8 bitroll ] map ]

Try it online!

Roll each code point 4 bits to the left, wrapping around after 8 bits.

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

Charcoal, 12 bytes

⭆S℅↨¹⁶⮌↨℅ι¹⁶

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

 S              Input string
⭆               Map over characters and join
         ι      Current character
        ℅       ASCII code
       ↨        Convert to base
          ¹⁶    Literal integer `16`
      ⮌         Reversed
   ↨            Convert from base
    ¹⁶          Literal integer `16`
  ℅             ASCII Character
                Implicitly print

If both parameters to Base are integers then the second is always the base but if one parameter is an array then the other is the base. This allows the two 16s to be naturally separated in the code thus avoiding an explicit separator.

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

R, 50 bytes

function(s,r=utf8ToInt(s))intToUtf8(16*r%%16+r/16)

Try it online!

Much more boring than my previous approach, but 7 bytes shorter.


Previous approach:

R, 57 bytes

function(s)intToUtf8(t(matrix(0:255,16))[utf8ToInt(s)+1])

Try it online!

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

StackCell (u32), 20

'.[@:'ÿ'␂+*'␐x/'ÿ&;] (due to an interpreter bug, programs are required to contain valid UTF-8 - the 255 bytes need to be converted to #FF to run this, losing two bytes; however, by the language's specification this is a well-formed program - therefore I have not counted the two lost bytes as part of my score; let me know if I need to) I have just fixed this interpreter bug

Explanation:

'.[@:'ÿ'␂+*'␐x/'ÿ&;]
'.                   Push a non-null byte (46) to the stack
  [                ] Loop until EOF
   @:                Input a byte (0xXY) and duplicate it
     'ÿ'␂+           Push the bytes 255 and 2 to the stack, and add them together
          *          Multiply the inputted character by 0x101 (-> 0xXYXY)
           '␐x       Push the byte 16 to the stack and swap it below the multiplied character
              /      Divide the character by 16 (-> 0xXYX)
               'ÿ&   Mask the character with 0xFF (-> 0xYX)
                  ;  Print the swapped character
\$\endgroup\$
2
\$\begingroup\$

K (ngn/k), 10 bytes

`c$16/|16\

Try it online!

  • 16\ treating the string input as if it is integers (corresponding to the ASCII codes), convert it to a base-16 representation
  • | reverse the digits
  • 16/ convert back from the base-16 representation
  • `c$ convert back to a character/string format and (implicitly) return
\$\endgroup\$
1
\$\begingroup\$

Burlesque, 13 bytes

m{**b6<-b6L[}

Try it online!

m{  # Map
 ** # Codepoint
 b6 # To hex
 <- # Reverse
 b6 # From hex
 L[ # To char
}
\$\endgroup\$
1
\$\begingroup\$

BQN, 17 bytes

16(@+⟜⌊÷˜+⊣×|)-⟜@

Try it at BQN online REPL

               -⟜@   # subtract null character (@) to get ASCII codepoints
16(           )       # now, with this as right arg and 16 as left arg:
             |        # right arg (codepoints) modulo left arg (16)
            ×         # multiplied by 
           ⊣          # left arg (16)
          +           # plus
        ÷˜            # right arg (codepoints) divided by left arg (16) 
      ⟜⌊              # now use the floor of this 
    @+                # to add to null character (@) to get ASCII characters
\$\endgroup\$
1
\$\begingroup\$

Desmos, 105 bytes

f(a)=[total(mod(floor(n/16^l),16)16^k/16^l)fori=[1...a.length]]
n=a[i]
k=floor(log_{16}(n+0^n))
l=[k...0]

Try it on Desmos!

Input and output are list of codepoints because Desmos doesn't support strings.

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

C (gcc), 70 66 60 bytes

-7 bytes thanks to @ceilingcat

main(t,v)char**v;{for(++v;t=**v;++*v)putchar(t>>4|t%16<<4);}

Try it online!

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

J-uby, 26 bytes

~:unpack&'H*'|~:pack&'h*' 

Try it online!

packing and unpacking turns out to be the same length as the optimal ruby solution.

J-uby, 37 bytes

:bytes|:*&(:*&257|~:>>&4)|~:pack&'c*'

Try it online!

J-uby, 52 bytes

:bytes|:*&(~:to_s&16|:reverse|~:to_i&16)|~:pack&'c*'

Try it online!

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

Red, 39 bytes

func[s][foreach c s[prin c * 16 % 255]]

Try it online!

Based on other similar answers. Thanks to @dingledooper and @UnrelatedString (I think) for discovering it.

\$\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.