# lol is an ambigram, dad isn't

Given a word, decide if it is an ambigram.

When rotated:

b > q
d > p
l > l
n > u
o > o
p > d
q > b
s > s
u > n
x > x
z > z


Assume only lowercase letters as input.

Test input:

this,
another,
lll,
lol,
dab,
dap,
wow,
ooi,
lollpopdodllol,


Ones That Should Return Truthy Values:

lll,
lol,
lollpopdodllol,


Note that input and output can be taken in any format (except for Standard Loopholes).

• This makes absolutely no sense to me. Can you work through an example perhaps? Jun 2, 2022 at 0:39
• I suggest a test case where the characters are rotationally symmetric but the word isn't, like pppd. This prevents simple subset solutions from passing all test cases. Jun 2, 2022 at 1:24
• pod is another truthy example. Jun 2, 2022 at 10:16
• a/e is a close match Jun 2, 2022 at 11:28
• These are not palindromes, they are ambigrams Jun 2, 2022 at 14:04

# Factor + flip-text, 34 bytes

[ dup flip-text "ʃ""l"replace = ]


Attempt This Online!

Factor has a vocabulary for ʇxǝʇ ᵷuᴉddᴉʃɟ, but for some reason it turns l into ʃ, so I have to change it back.

# Python, 70 69 bytes

-1 byte thanks to Kyle G

lambda s:[i*(i in b'losxz')or b'dbq0pnu0'[i%8]for i in s][::-1]==[*s]

Attempt This Online!

Accepts a bytes object as input.

## Explanation

[... for i in s] Build an array mapping each letter i of s to...

i*(i in b'losxz') ... i, if i is one of the letters that should remain unchanged, or...

b'dbq0pnu0'[i%8] ... its rotation. Since the rotatable letters have unique code points mod 8, we can use that to index into a string of possible rotations. Note that multiple letters may map to a given letter (e.g. u and e both map to n), but that's fine since only the rotatable letters will form a reversible map (e.g. b->q->b)

[...][::-1]==[*s] Reverse the new array and compare to the original string, unpacked into an array.

• @solid.py I added an explanation. The most interesting golfing tricks are probably the ord(i)%8 and the string unpacking ([*s]) Jun 2, 2022 at 14:37
• you can save a byte by requiring the input to be a bytes or bytearray object instead of a string Jun 2, 2022 at 18:55

# Charcoal, 28 bytes

θ⟲Ｔ⁴≔⪫ＫＡωη⎚⁼θΦη№bdlnopqsuxzι


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

θ


Print the input string to the canvas.

⟲Ｔ⁴


Rotate the canvas by 180°, transforming the characters as required.

≔⪫ＫＡωη


Read the transformed rotated string back from the canvas.

⎚


Clear the canvas.

⁼θΦη№bdlnopqsuxzι


Compare the two strings, and check that all of the letters in the string were valid. (This check costs half of the byte-count, ugh.)

• right tool for the job ;)
– att
Jun 2, 2022 at 1:22

# Vyxal, 23 bytes

ka«ƛG₈₀2ǔvȯF±ɾF)ǍrT«ĿṘ⁼


## How?

ka«×¢₇}Ẇ₇]↲βjEβ&ɾṗµ«ĿṘ⁼
ka                      # Push the lowercase alphabet
«×¢₇}Ẇ₇]↲βjEβ&ɾṗµ«    # Push compressed string "bqapaaaaaaalauodbasanaaxaz"
Ŀ   # Transliterate the (implicit) input from the lowercase alphabet to that
Ṙ  # Reverse
⁼ # Is it equal to the (implicit) input?


A port of Kevin's 05AB1E answer is 23 bytes as well:

# Vyxal, 23 bytes

Ḃ«ƛ≠ȧ‹«:£ḂĿka¥«g₴Ṡ«JFF=


Try it Online!

• You forgot z in your compressed string. Jun 2, 2022 at 8:21

# JavaScript (ES6), 68 bytes

Saved 1 byte thanks to @Shaggy

Returns $$\0\$$ or $$\1\$$.

s=>[...s].map(c=>o=S[S.search(c)^1]+o,S=o='bqdpnulloossxxzz')|o==s+S


Try it online!

• 68 bytes Jun 2, 2022 at 10:21
• @Shaggy Ah, nice one! Jun 2, 2022 at 12:30

# C (gcc): 172 171 bytes

Edit: Thanks to ceilingcat for shortening the code to 172 171 bytes by using some nifty tricks!

Hello, first time posting code golf here! Criticism is welcome. :)

d[]={=81,0,80,=76,0,85,79,68,68,0,83,0,78,0,0,88,0,90};main(c,v)int**v;{for(char*s,*e;--c;s>e&&puts(v[c]))for(e=index(s=v[c],0);s<e--&d[*e]-*s+d[*s]+64==*e;)s++;}


Try it online (with explanation)

Explanation:


/**
* Thanks to ceilingcat for shortening the code to 171 bytes by using some nifty tricks!
*/

/**
* Stores the flipped equvilent of a character in upper case ASCII
* (sorry to anyone using EBCDIC) where the index is the character (lower case)
* and the value is the flipped equivalent of the character.
* The reason I used upper case is because writing many ['b'] = 'q' is more
* expensive (space-wise) than writing  = 81 and converting them each time
* (by adding or subtracting 32).
*/
d[]={=81,0,80,=76,0,85,79,68,68,0,83,0,78,0,0,88,0,90};
main(c,v)int**v;{
// Prints string if the 2 pointers crossed the middle which would
// mean the second loop never exited (is a palindrome) at the end.
for(char*s,*e;--c;s>e&&puts(v[c]))
// 'e=index(s=v[c],0)' gets the pointers for the first and last
// character of the string.
// Exits the loop when character '*s' is not equal to flipped character '*e' and
// vice versa or the 2 pointers cross at the middle of the string.
for(e=index(s=v[c],0);s<e--&d[*e]-*s+d[*s]+64==*e;)
s++;
}


• Welcome to Code Golf, and nice first answer! In our site, we allowed that input can be a function argument and output can be what the function returns, so that can cut you some bytes (see my answer for example). Also, take a look at Tips for golfing in C. Jun 3, 2022 at 14:59
• @ceilingcat Wow, great find! I was sure that I squeezed out every byte. :P Jun 7, 2022 at 9:24

# J-uby, 1531485655 53 bytes

:=~&(:~|~:tr&"pq#{?p*9}lpuodbpspnppxpp"&[*?a..?z]*'')


Try it online!

Just learning J-uby so this definitely is terrible.

Edit: I knew it. -95 bytes thanks to Steffan

• sorry, 53 Jun 2, 2022 at 3:42
• @Steffan why sorry? I can't thank you enough for shaving 95 bytes Jun 2, 2022 at 3:44
• bcs i have to say my golfs in 3 different comments lol Jun 2, 2022 at 3:45
• @Razetime I try to add, but it says suggested edits are not allowed (I don't have enough reputation yet). Could you add my answer in? Jun 2, 2022 at 3:48

# R, 56 bytes

\(s)all(chartr("a-y","AqCpE-KlMuodbRsTnVWxY",s)==rev(s))

Attempt This Online!

Takes input as a vector of characters.

Translates all characters from the dictionary in the question to their counterparts and all others to uppercase equivalents (comparison is case sensitive in R, so those will break the equality).

# Regex (Perl / PCRE2 / Boost / Pythonregex), 65 bytes

^(([losxz])((?1)\2|)|b(?1)q|q(?1)b|d(?1)p|p(?1)d|n(?1)u|u(?1)n|)$ Try it online! - Perl Try it online! - PCRE2 Try it online! - Boost Attempt This Online! - Python import regex This is a straight port of my answer to Numbers with Rotational Symmetry. ^ # Assert this is the start of the string ( # Define (?1) recursive subroutine call ([losxz]) # \2 = match a char that is its own rotation ((?1)\2|) # Optionally match (?1) followed by \2 | b(?1)q | q(?1)b | d(?1)p | p(?1)d | n(?1)u | u(?1)n | # Match an empty string at the center of an even-length string )$              # Assert this is the end of the string


If there just one more matched pair of distinct characters, e.g. ae, another method would win by 3 bytes:

^(([losxz])((?1)\2|)|a(?1)e|e(?1)a|b(?1)q|q(?1)b|d(?1)p|p(?1)d|n(?1)u|u(?1)n|)$^((?=([losxz]|a.*e|e.*a|b.*q|q.*b|d.*p|p.*d|n.*u|u.*n)(?<=(.))).((?1)\3|)|)$


But instead it loses by 1 byte:

^(([losxz])((?1)\2|)|b(?1)q|q(?1)b|d(?1)p|p(?1)d|n(?1)u|u(?1)n|)$^((?=([losxz]|b.*q|q.*b|d.*p|p.*d|n.*u|u.*n)(?<=(.))).((?1)\3|)|)$


## Regex (Ruby), 72 bytes

^((?=([losxz]|b.*q|q.*b|d.*p|p.*d|n.*u|u.*n)(?<=(.))).(\g<1>\k<3+0>|)|)$ Try it online! In this port, unlike the Perl/PCRE2/Boost/Pythonregex version, the alternative method wins by 5 bytes: ^(([losxz])(\g<1>\k<2+0>|)|b\g<1>q|q\g<1>b|d\g<1>p|p\g<1>d|n\g<1>u|u\g<1>n|)$
^((?=([losxz]|b.*q|q.*b|d.*p|p.*d|n.*u|u.*n)(?<=(.))).(\g<1>\k<3+0>|)|)$ ## Regex (.NET), 75 bytes ^((?=([losxz]|b.*q|d.*p|n.*u|p.*d|q.*b|u.*n)(?<=(.))).?)+(?<-3>\3)*(?(3)^)$


Try it online!

^((?=([losxz]|b.*q|q.*b|d.*p|p.*d|n.*u|u.*n)(?<=(.))).?(?=.*(\3(?(4)\4))))*\4?$ Try it online! - Perl Try it online! - PCRE1 Try it online! - PCRE2 Try it online! - .NET ## Regex (Perl / PCRE / Java / .NET), 86 bytes ^((?=([losxz]|b.*q|q.*b|d.*p|p.*d|n.*u|u.*n)(?<=(.))).?(?=.*(\3(\6\4|(?!\6))())))*\4?$


Try it online! - Perl
Try it online! - PCRE1
Try it online! - PCRE2
Try it online! - Java
Try it online! - .NET

# APL+WIN, 44 37 bytes

-7 bytes thanks to OVS

Prompts for text

t≡⌽'qpluodbsnxz '['bdlnopqsuxz'⍳t←,⎕]


Try it online! Thanks to Dyalog Classic APL

• I'm guessing APL+WIN doesn't have ≡, but ∧/t=⌽ ... should work?
– ovs
Jun 2, 2022 at 7:56

# 05AB1E, 23 bytes

Â.•1â‹ƒ•©Â‡A®.•(ÖÆ•«ммQ


A port of @Steffan's Vyxal answer is 23 bytes as well:

ÂA.•1ηλ¥nô[˜ζÕPζeí=Ω•‡Q


Explanation:

Â          # Bifurcate the (implicit) input-string, short for Duplicate & Reverse
.•1â‹ƒ•   # Push the compressed string "bdunpq"
©        # Store it in variable ® (without popping)
Â       # Bifurcate it
‡      # Transliterate all "bdunpq" to "qpnudb"
A         # Push the lowercase alphabet
®        # Push string ®
.•(ÖÆ•« # Append compressed string "losxz"
м      # Remove all those characters from the alphabet: "acefghijkmrtvwy"
м     # Remove all those characters from the modified input
Q    # Check if it's equal to the input-string
# (after which the result is output implicitly)

Â          # Bifurcate the (implicit) input-string, short for Duplicate & Reverse
A         # Push the lowercase alphabet "abcdefghijklmnopqrstuvwxyz"
.•1ηλ¥nô[˜ζÕPζeí=Ω•
# Push the compressed string  "bqapaaaaaaalauodbasanaaxaz"
‡       # Transliterate the alphabet to these characters
Q      # Check if it's equal to the input-string
# (after which the result is output implicitly)


See this 05AB1E tip of mine (section How to compress strings not part of the dictionary?) to understand why .•1â‹ƒ• is "bdunpq"; .•(ÖÆ• is "losxz"; and .•1ηλ¥nô[˜ζÕPζeí=Ω• is "bqapaaaaaaalauodbasanaaxaz".

# PARI/GP, 70 bytes

s->Vecrev(s)==[Vec("qpluodbsnxz")[i-96]|i<-Vecsmall(s)]

Attempt This Online!

# C (gcc), 835 bytes

I never was very good at golf...

#include "string.h"
#include "stdio.h"
char z = {{'b','q'},{'d','p'},{'n','u'},{'l','l'},{'o','o'},{'s','s'},{'x','x'},{'z','z'},};
void main(int argc, char *argv[])
{
int n = argc-1;
for (int x = 1; x <= n; x++){
int p = 0;
int l = strlen(argv[x]);
char r[l+1];
for (int i = l; i > 0; i--){
char c = argv[x][i-1];
int v = 0;
for (int a = 0; a < 8; a++){
if (c == z[a]){
c = z[a];
v = 1;
} else if (c == z[a]){
c = z[a];
v = 1;
}
}
if (!v) {p = 1;}
r[l-i] = c;
}
r[strlen(argv[x])+1] = '\0';
printf("%d\n", memcmp(r, argv[x], l) == 0 && p == 0);
}
}


Try it online!

# C (gcc), 1238 bytes

#include "string.h"
#include "stdio.h"

char letters_to_rotate = {
{'b', 'q'},
{'d', 'p'},
{'n', 'u'},
{'l', 'l'},
{'o', 'o'},
{'s', 's'},
{'x', 'x'},
{'z', 'z'},
};

void main(int argc, char *argv[])
{
int num_inputs = argc-1;
for (int x = 1; x <= num_inputs; x++){
int invalid_char_present = 0;
int inputlength = strlen(argv[x]);
char rotated[inputlength+1];
for (int i = inputlength; i > 0; i--){
char c = argv[x][i-1];
int valid_char = 0;
for (int a = 0; a < 8; a++){
if (c == letters_to_rotate[a]){
c = letters_to_rotate[a];
valid_char = 1;
}
else if (c == letters_to_rotate[a]){
c = letters_to_rotate[a];
valid_char = 1;
}
}
if (!valid_char) {invalid_char_present = 1;}
rotated[inputlength-i] = c;
}
rotated[strlen(argv[x])+1] = '\0';
if (memcmp(rotated, argv[x], inputlength) == 0 && invalid_char_present == 0){
printf("%s: True\n", argv[x]);
} else {
printf("%s: False\n", argv[x]);
}
}
}


Try it online!

• Welcome to Code Golf! You've golfed things pretty well so far, but there's a few more easy things you can do to lower your byte count. You have a lot of whitespace, which is almost all unnecessary. Indentation, line breaks, spaces before and after operators and keywords, etc. After that, you might want to check out Tips for golfing in C to see if there's any more tricks you can use, like clever for loop stuff. Jun 3, 2022 at 0:01
• 206 bytes Jun 9, 2022 at 15:18

# Rust, 135 130 bytes

let m="bqdpnulloossxxzz";let f=|s:&str|!s.chars().zip(s.chars().rev()).any(|(a,b)|m.find(a).or(Some(99))!=m.rfind(b).map(|x|x^1));


I'm pretty new to golfing so I hope I did it alright.

I'm still pretty unhappy with how verbose the code to fix the options is.

• Try it online! - link where others can run your program. Feel free to edit it into your answer if you want Jun 2, 2022 at 20:09

# T-SQL, 84 bytes

Returns 1 when palindrome, otherwise 0

PRINT IIF(translate(@,'bqdpnu','qbpdun')<>reverse(@)or
@ like'%[^qbpdunolsxz]%',0,1)


Try it online

# Oracle SQL, 68 bytes

o=reverse(translate(o,'bdlnopqsuxzacefghijkmnrstuvwy','qpluodbsnxz'))


Tested with

with w as (
select column_value o
from
ku$_vcnt( 'this', 'another', 'lll', 'lol', 'dad', 'dab', 'dap', 'wow', 'ooi', 'lollpopdodllol' )) select o from w where o=reverse(translate(o,'bdlnopqsuxzacefghijkmnrstuvwy','qpluodbsnxz'));  • your solution will not return an answer. You have just included the "WHERE" clause as answer. Jun 3, 2022 at 12:45 # Bash, 71 bytes f()(a=bdlnopqsuxz;[ "echo$1|tr $a qpluodbsnxz|tr -dc$a|rev" = $1 ])  Try it online! Using a more compact tr command as inspired by pajonk's answer: # Bash, 58 bytes f()([ "echo$1|tr a-y AqCpE-KlMuodbRsTnVWxY|rev" = $1 ])  Try it online! • Welcome to Code Golf! Jun 3, 2022 at 20:49 • Wouldn't this be shorter as a full program (script) rather than a function? Also, I think returning via success code is an allowed output format for a boolean function, in case that helps. – Neil Jun 6, 2022 at 16:17 # PowerShell, 7268 51 bytes !(($s=$args)|?{'dbqspn x z l uo'[$_%16]-$s[--$i]})


Try it online!

The script assumes an input does not contains space chars. I guess It is good constraint for a word.

The script takes a splatted string and performs double check for each symbol from the string.

The algorithm for the hash string dbqspn x z l uo is: Place an ambigram char at the position (ASCII % 16).

Source char ASCII % 16 Ambigram char
p 112 0 d
q 113 1 b
b 98 2 q
s 115 3 s
d 100 4 p
u 117 5 n
x 120 8 x
z 122 10 z
l 108 12 l
n 110 14 u
o 111 15 o

# Retina 0.8.2, 56 bytes

$¶$
Tb\dl-qsuxzlq\p\l_u\o\dbsnxz_^.+
+(.)¶\1
¶
^¶$ Try it online! Link includes test cases. Explanation: $
¶$  Duplicate the input. Tb\dl-qsuxzlq\p\l_u\o\dbsnxz_^.+  Individually rotate the characters of the first copy, but delete characters that aren't rotatable. (d, l, o and p need to be quoted when not used as part of a range.) +(.)¶\1 ¶  Compare the last character of the first copy with the first character of the second, removing both if they are equal. Repeat until either they don't match or there aren't any left. ^¶$


If there aren't any left then the original input was a rotational palindrome.

# K (ngn/k), 30 bytes

{x~|v(|v:"lnopqsxzxsbdoul")?x}


Try it online!

I took the shortest ambigram which contained all the possible characters and made it into a lookup string: locate where each character of the input is in the string, and then take one character from the reversed version to construct the rotated character. Finally, reverse it and compare against the original input.

# JavaScript, 88 bytes

s=>s==[...s].map(x=>'losxz'.includes(x)?x:(r='budpnq')[5-r.indexOf(x)]).reverse().join


Try it online!

# Stax, 23 bytes

Ç╥Vⁿ2n♣¿æ∞╙♥>5H╜ÜN¡¼}╝î


Run and debug it

# Perl 5-pal, 52 bytes

y/bqdpunloszx//dc;y/bqdpun/qbpdnu/;\$_="@F"eq reverse


Try it online!

# Scala, 118 bytes

Golfed version. Try it online!

def f(s:Seq[Byte])=s.map(i=>if("losxz".contains(i.toChar))i else"dbq0pnu0".charAt(i%8).toByte).reverse.sameElements(s)


Ungolfed version.

object Main {

def f(s: Array[Byte]): Boolean = {
val result = s.map(i => if ("losxz".contains(i.toChar)) i else "dbq0pnu0".charAt(i % 8).toByte).reverse
result.sameElements(s)
}

def main(args: Array[String]): Unit = {
println(f("bq".getBytes))               // true
println(f("lol".getBytes))              // true
println(f("lollpopdodllol".getBytes))   // true
`