# Let's golf a BIBABOBU decoder

While I was traveling in the future, I noticed a funny game among kids circa 2275. When they don't want their great-great-great-great-grand parents to understand what they're saying, they use the BIBABOBU speak. Obviously, I couldn't understand anything either with my pre-cyborg era brain and I felt (or technically: I will feel) really silly. So, I'd need a decoder for my next visit.

## BIBABOBU?

While it's been deprecated for a long time, ASCII is still commonly used in the pop culture of 2275 and this language is based upon it.

A string is BIBABOBU-encoded that way:

• Convert all characters to their ASCII codes.
• Take the 2-digit hexadecimal representation of each code and convert them using the following table:

0: BI  4: BIDI  8: BADI  C: BODI
1: BA  5: BIDA  9: BADA  D: BODA
2: BO  6: BIDO  A: BADO  E: BODO
3: BU  7: BIDU  B: BADU  F: BODU

### Example

"Hello!" → 48 65 6C 6C 6F 21 → "BIDIBADI BIDOBIDA BIDOBODI BIDOBODI BIDOBODU BOBA"

However, the corresponding input would be given without any space to mimic the monotonous intonation that kids are using to make this even harder to understand without implants:

## Clarifications and rules

• Remember that I need a decoder, not an encoder.
• Decoded characters are guaranteed to be in the range [ 32...126 ].
• The input is guaranteed to contain en even number of BIBABOBU-encoded hexadecimal digits.
• You may take input in either full lowercase or full uppercase. Mixed cases are not allowed.
• Because bit flips are quite common during a time travel, this is in order to minimize the risks.

## Test cases

NB: Linefeeds are used below for formatting purposes only. You are not supposed to handle them.

Input:
BIDABIDIBIDOBIDABIDUBUBIDUBIDI

Output:
Test

Input:
BIDABIDUBIDOBIDABIDOBODIBIDOBUBIDOBODUBIDOBODABIDOBIDABOBIBIDUBIDIBIDOBODUBOBIBUBOBUBOBUBI
DUBUBIDABOBA

Output:
Welcome to 2275!

Input:

Output:
Hello, Time Traveler! You look so funny!

Input:
DIBOBIBIDUBIDABIDOBODOBIDOBIDIBIDOBIDABIDUBOBIDUBUBIDUBIDIBIDOBABIDOBODOBIDOBIDIBOBIBIDUBI

Output:
And you don't understand what I'm saying, do you? Ha ha ha!
• @StewieGriffin These damn kids are mischievous... :-/ – Arnauld Aug 29 '18 at 9:04
• By the way, I find the story really really unlikely! I wouldn't be surprised if it was just a dream you had... might you have a CO leak in your house? – Stewie Griffin Aug 29 '18 at 9:09
• Ah... That would also explain the ponies riding rainbows in my living room! – Arnauld Aug 29 '18 at 9:13
• One could argue that golfed code increases the severity of a bit-flip (less redundancy within the code) even if it decreases the frequency of a bit-flip... but whatever :) - Nice challenge! – JayCe Aug 29 '18 at 12:53
• @JayCe True. I was more thinking along the lines: the smaller the code, the more redundancy you can get by storing multiple copies. – Arnauld Aug 29 '18 at 13:01

# 05AB1E, 3635 33 bytes

Saved 1 byte thanks to Mr.Xcoder
Saved 2 bytes thanks to KevinCruijssen

Try it online! or as a Test Suite

Explanation

ć©¡                                 # extract the head ("B") and split input on it
®ì                              # prepend "B" to each
®D                            # push 2 copies of "B"
…IAO©â                      # cartesian product with "IAO"
'D«                   # append "D" to each
‚˜                 # add the leftover "B" to the list
®'U«â            # cartesian product with "IAOU"
J           # join each to string
sk         # get the index of each word of the input in this list
h        # convert each to hex
2ôJ     # format as pairs of chars
H    # convert to int
çJ  # convert from ascii-codes to string
• I believe 'B©¡¦®ì®D…IAO©â'D«‚˜®'U«âJskh2ôJHçJ works for 35 bytes. – Mr. Xcoder Aug 29 '18 at 10:39
• @Mr.Xcoder: Ah, of course. Nice reuse of ©. Thanks :) – Emigna Aug 29 '18 at 10:45
• -2 bytes changing the leading 'B to ć and removing the ¦, since the input will always start with a 'B'. – Kevin Cruijssen Aug 29 '18 at 20:25
• @KevinCruijssen: Ooh, good idea. I hadn't considered ć. Thanks! – Emigna Aug 30 '18 at 6:21
• Now let's get back at those kids and see if they understand that ! – Aaron Aug 30 '18 at 8:28

# Jelly, 262423222017 15 bytes

ṣḢO^1%9%4Ḅḅ⁴b⁹Ọ

Try it online!

### How it works

ṣḢO^1%9%4Ḅḅ⁴b⁹Ọ  Main link. Argument: s (string)

Ḣ               Head; remove and yield the first character of s.
ṣ                Split s at occurrences of the result ('B').
O              Ordinal; map "IAOUD" to A1 := [73, 65, 79, 85, 68].
^1            Bitwise XOR 1; map A1 to A2 := [72, 64, 78, 84, 69].
%9          Modulo 9; map A2 to A3 := [0, 1, 6, 3, 6].
%4        Modulo 4; map A3 to A4 := [0, 1, 2, 3, 2].
So far, we've mapped "BX" to [x] and "BXDY" to [x, 2, y],
where x/y = 0, 1, 2, 3 when X/Y = I, A, O, U.
Ḅ       Unbinary; map [x] to x and [x, 2, y] to 4x + 2×2 + y = 4(x + 1) + y.
ḅ⁴     Convert the resulting array from base 16 to integer.
b⁹   Convert the resulting integer to base 256.
Ọ  Unordinal; replace code points with their characters.

Try it online!

# Python 2, 1009796 95 bytes

-1 byte thanks to ovs
-1 byte thanks to G B

lambda w:''.join(' 1023546798abdcef'[int(c,35)/7%77%18]for c in w.split('B')[1:]).decode("hex")

Try it online!

# Perl 5 -p, 67 bytes

for$p(<{B,0D,1D,2D}{I,A,O,U}>){s/$p/chr$i+48/ge;$i++}$_=pack'H*',$_

Try it online!

# 05AB1E (legacy), 686560 59 bytes

.•5Ç¿ÆΓ•2ô.•1ÒKá ¸ΓìŸÆtô_zTºγ„KRI‰ιë†½òι•4ô«I¬©¡®ìkh2ôJHçJ

Input is in lowercase.

-3 bytes implicitly thanks to @Emigna changing 'b¡εg>}s£ to 'b©¡®ì.

Also, can definitely be golfed with something smarter than the huge compressed strings. Will take another look later on. Shorter answer already provided by @Emigna, so make sure to upvote him!

Explanation:

.•5Ç¿ÆΓ•      # Compressed string "bibabobu"
2ô    # Split in parts of 2
#  → ["bi","ba","bo","bu"]
.•1ÒKá ¸ΓìŸÆtô_zTºγ„KRI‰ιë†½òι•
4ô    # Split in parts of 4
«             # Merge both lists together
I¬©¡          # Take the input and split on the head (always 'b')
®ì        # And prepend a 'b' to each item again
k             # Map each item to the index of the first list
#    → [4,8,6,5,6,12,6,12,6,15,2,1]
h            # Convert everything to hexadecimal
#  i.e. [4,8,6,5,6,12,6,12,6,15,2,1]
#   → ["4","8","6","5","6","C","6","C","6","F","2","1"]
2ôJ         # Split it in parts of 2 and join them together
#  i.e. ["4","8","6","5","6","C","6","C","6","F","2","1"]
#   → ["48","65","6C","6C","6F","21"]
H        # Convert that from hexadecimal to an integer
#  i.e. ["48","65","6C","6C","6F","21"] → [72,101,108,108,111,33]
ç       # And take its ASCII value
#  i.e. [72,101,108,108,111,33] → ["H","e","l","l","o","!"]
J      # Then join everything together (and output implicitly)
#  i.e. ["H","e","l","l","o","!"] → "Hello!"

# Perl 6, 88 86 84 bytes

{S:g{B(.*?)B(.D.|.)}=chr :16[$/.map:{first :k,~$_,("",|<ID AD OD UD>X~ <I A O U>)}]}

Try it online!

# R, 141 135 bytes

function(x,y="I":"A":"O")intToUtf8(matrix(match(el(strsplit(gsub("D","",x),"B"))[-1],paste0(rep("":y,e=4),y:"U"))-1,,2,T)%*%16:1)
":"=c

Try it online!

Thanks to JayCe for saving 6 bytes!

Using some modular magic is likely to be shorter, but I'm pretty happy with this as a naive first pass.

• Nice ! My favorite trick saves 6 bytes - inspired by your comment to an answer of mine the other day. – JayCe Aug 29 '18 at 19:45
• @JayCe very nice and neat! Even using it with the precedence over %*% I see. :-) You can also put : as a function argument in case you wanted to use this in tandem with something else! – Giuseppe Aug 29 '18 at 19:57
• That's right - I always tend to forget about the backquotes. – JayCe Aug 29 '18 at 20:15

# Japt, 4329 28 bytes

Unsurprisingly, a port of Dennis' Jelly solution works out much shorter.

Outputs an array of characters.

Åqbu)®¬®c ^1 %9%4Ãì2Ãò ®ìG d

Try it

## Original, 42 bytes

Åqb £bbidbad¾dò3n)ïiaq)m¬bXibÃò ®ìG d

Try it

### Explanation

Åqb £bbidbad¾dò3n)ïiaq)m¬bXibÃò ®ìG d
Å                                              :Slice off the first character
qb                                            :Split on "b"
£                                          :Map
bbidbad¾d                               :  Compressed string "bbidbadbod"
ò3n)                           :  Partition at every 3rd character from the end (["b","bid","bad","bod"])
ï                          :  Cartesian product
ia                      :   Compressed string "iaou"
q                    :   Split
)                   :  End Cartesian product
m                  :  Map
¬                 :   Join
b                :  Index of
X               :   Current element
ib             :   Prepend "b"
Ã            :End map
ò           :Partition at every second element
®         :Map
ìG       :  Convert from base-16 digit array to base-10 integer
d     :  Get the character at that codepoint

# C (gcc), 181138 136 bytes

Hopefully there will be a C compiler in the future to compile this! :-)

Thanks to Max Yekhlakov and ceilingcat for the suggestions.

v,t,c,d;f(char*s){for(v=t=c=0;d=*s++;)t+=d==66?v=v*16+t,++c>2?v=!putchar(v),c=1:0,-t:d-65?d-79?d-68?d-85?0:3:4+t*3:2:1;putchar(v*16+t);}

Try it online!

In case the C compiler of the future only understands BIBABOBU-ified ASCII :-)

(Encoder Try it online!)

• 179 bytes - Try it online! – Max Yekhlakov Aug 30 '18 at 10:34
• Suggest c=printf(&v),v=0 instead of v=!putchar(v),c=1 – ceilingcat Oct 19 '18 at 6:44

# JavaScript (Node.js), 131 128 bytes

s=>unescape(s.replace(/B.(D.)?/g,(f,s)=>(-~g(f[1])*4*!!s+g((s||f)[1])).toString(16),g=c=>'IAOU'.search(c)).replace(/../g,'%$&')) Try it online! Link includes test cases. Alternative version, also 131 bytes: s=>unescape(s.replace(/B.(D.)?/g,s=>(-~g(s[1])*4*!!s[3]+g(s[3]||s[1])).toString(16),g=c=>'IAOU'.search(c)).replace(/../g,'%$&'))

Try it online! Link includes test cases. Edit: Saves 3 bytes thanks to @Shaggy.

• Using unescape() is a nice idea. – Arnauld Aug 29 '18 at 9:43
• indexOf -> search to save a byte. – Shaggy Aug 29 '18 at 12:14
• Also, it doesn't look like you need to assign the RegEx to r. – Shaggy Aug 29 '18 at 12:23
• @Shaggy Whoops, that's a left-over from a previous iteration. Thanks! – Neil Aug 29 '18 at 12:28

toString o$o@ // convert to string (make numbers (make digits)) Try it online! Explained: # PHP, 119 bytes foreach(explode(B,$argn)as$i=>$m)($v=$v*16+4*strpos(XIAO,$m[-3]?:B)+strpos(IAOU,$m[-1]?:B))?$i&1||print chr($v&=255):0;

assumes uppercase input. Run as pipe with -nR or try it online.

requires PHP 7.1

# Python 3, 199 bytes

import re
lambda s:''.join(eval(re.sub(r'(\d+), (\d+)',r'chr(16*\1+\2)',str(eval(s.replace('I','1').replace('A','2').replace('O','3').replace('U','4').replace('B',',-1+').replace('D','*4+')[1:])))))

Not the shortest but without loops.