24
\$\begingroup\$

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,
dad,
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).

\$\endgroup\$
13
  • 7
    \$\begingroup\$ This makes absolutely no sense to me. Can you work through an example perhaps? \$\endgroup\$
    – chunes
    Commented Jun 2, 2022 at 0:39
  • 8
    \$\begingroup\$ 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. \$\endgroup\$
    – chunes
    Commented Jun 2, 2022 at 1:24
  • 2
    \$\begingroup\$ pod is another truthy example. \$\endgroup\$
    – stanri
    Commented Jun 2, 2022 at 10:16
  • 2
    \$\begingroup\$ a/e is a close match \$\endgroup\$ Commented Jun 2, 2022 at 11:28
  • 34
    \$\begingroup\$ These are not palindromes, they are ambigrams \$\endgroup\$ Commented Jun 2, 2022 at 14:04

25 Answers 25

19
\$\begingroup\$

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.

\$\endgroup\$
12
\$\begingroup\$

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.

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

Charcoal, 28 bytes

θ⟲T⁴≔⪫KAωη⎚⁼θΦη№bdlnopqsuxzι

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

θ

Print the input string to the canvas.

⟲T⁴

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

≔⪫KAωη

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.)

\$\endgroup\$
1
  • 5
    \$\begingroup\$ right tool for the job ;) \$\endgroup\$
    – att
    Commented Jun 2, 2022 at 1:22
10
\$\begingroup\$

Vyxal, 23 bytes

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

Try it online or verify all test cases.

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!

\$\endgroup\$
1
  • \$\begingroup\$ You forgot z in your compressed string. \$\endgroup\$ Commented Jun 2, 2022 at 8:21
7
\$\begingroup\$

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!

\$\endgroup\$
2
  • \$\begingroup\$ 68 bytes \$\endgroup\$
    – Shaggy
    Commented Jun 2, 2022 at 10:21
  • 2
    \$\begingroup\$ @Shaggy Ah, nice one! \$\endgroup\$
    – Arnauld
    Commented Jun 2, 2022 at 12:30
6
\$\begingroup\$

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).

\$\endgroup\$
6
\$\begingroup\$

C (gcc): 171 143 bytes

Edit: Thanks to ceilingcat for shortening the code to 171 160 bytes by using some nifty tricks! I did some simple cleanups to reduce it further to 143 bytes.

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

d[]=L"q.p.......l.uodb.s.n..x.z";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-98]-*s+d[*s-98]==*e;)s++;}

Try it online (with explanation)

Explanation:



/**
 * Thanks to ceilingcat for shortening the code to 160 bytes by using some nifty tricks!
 * Did some cleanups to reduce it to 143 bytes.
 */

/**
 * Stores the flipped equivalent of a character in ASCII (sorry to anyone using EBCDIC)
 * in an array represented as a string. To get the index of the flipped character simply
 * subtract 98 (ASCII for 'b') from the ASCII value of the character.
 * '.' is just padding.
 */
d[]=L"q.p.......l.uodb.s.n..x.z";
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-98]-*s+d[*s-98]==*e;)
                        s++;
}

\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$
    – badatgolf
    Commented Jun 3, 2022 at 14:59
  • \$\begingroup\$ @ceilingcat Wow, great find! I was sure that I squeezed out every byte. :P \$\endgroup\$ Commented Jun 7, 2022 at 9:24
5
\$\begingroup\$

J-uby, 153 148 56 55 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

\$\endgroup\$
6
  • 2
    \$\begingroup\$ sorry, 53 \$\endgroup\$
    – naffetS
    Commented Jun 2, 2022 at 3:42
  • \$\begingroup\$ @Steffan why sorry? I can't thank you enough for shaving 95 bytes \$\endgroup\$
    – badatgolf
    Commented Jun 2, 2022 at 3:44
  • 1
    \$\begingroup\$ bcs i have to say my golfs in 3 different comments lol \$\endgroup\$
    – naffetS
    Commented Jun 2, 2022 at 3:45
  • 2
    \$\begingroup\$ please do add your answer to the LoTM thread \$\endgroup\$
    – Razetime
    Commented Jun 2, 2022 at 3:46
  • \$\begingroup\$ @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? \$\endgroup\$
    – badatgolf
    Commented Jun 2, 2022 at 3:48
4
\$\begingroup\$

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.

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

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!

Regex (Perl / PCRE / .NET), 79 bytes

^((?=([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

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

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

\$\endgroup\$
1
  • \$\begingroup\$ I'm guessing APL+WIN doesn't have , but ∧/t=⌽ ... should work? \$\endgroup\$
    – ovs
    Commented Jun 2, 2022 at 7:56
3
\$\begingroup\$

05AB1E, 23 bytes

Â.•1⋃•©Â‡A®.•(ÖÆ•«ммQ

Try it online or verify all test cases.

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

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

Try it online or verify all test cases.

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".

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

PARI/GP, 70 bytes

s->Vecrev(s)==[Vec("`q`p```````l`uodb`s`n``x`z")[i-96]|i<-Vecsmall(s)]

Attempt This Online!

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

C (gcc), 835 bytes

I never was very good at golf...

#include "string.h"
#include "stdio.h"
char z[8][2] = {{'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][0]){
                    c = z[a][1];
                    v = 1;
                } else if (c == z[a][1]){
                    c = z[a][0];
                    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!

Original, much more readable version:

C (gcc), 1238 bytes

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

char letters_to_rotate[8][2] = {
{'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][0]){
                    c = letters_to_rotate[a][1];
                    valid_char = 1;
                }
                else if (c == letters_to_rotate[a][1]){
                    c = letters_to_rotate[a][0];
                    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!

\$\endgroup\$
2
  • 3
    \$\begingroup\$ 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. \$\endgroup\$ Commented Jun 3, 2022 at 0:01
  • \$\begingroup\$ 206 bytes \$\endgroup\$
    – ceilingcat
    Commented Jun 9, 2022 at 15:18
3
\$\begingroup\$

Retina 0.8.2, 56 bytes

$
¶$`
T`b\dl-qsuxzl`q\p\l_u\o\dbsnxz_`^.+
+`(.)¶\1
¶
^¶$

Try it online! Link includes test cases. Explanation:

$
¶$`

Duplicate the input.

T`b\dl-qsuxzl`q\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.

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

T-SQL, 84 bytes

Returns 1 when palindrome, otherwise 0

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

Try it online

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

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'));
\$\endgroup\$
1
  • \$\begingroup\$ your solution will not return an answer. You have just included the "WHERE" clause as answer. \$\endgroup\$ Commented Jun 3, 2022 at 12:45
3
\$\begingroup\$

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!

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Golf! \$\endgroup\$ Commented Jun 3, 2022 at 20:49
  • \$\begingroup\$ 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. \$\endgroup\$
    – Neil
    Commented Jun 6, 2022 at 16:17
3
\$\begingroup\$

PowerShell, 72 68 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
\$\endgroup\$
2
\$\begingroup\$

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.

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

JavaScript, 88 bytes

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

Try it online!

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

Stax, 23 bytes

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

Run and debug it

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

Perl 5 -pal, 52 bytes

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

Try it online!

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

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
    println(f("dad".getBytes))              // false
    println(f("wow".getBytes))              // false
    println(f("eon".getBytes))              // false
    println(f("pppd".getBytes))             // false
  }
}

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

Zsh, 41 bytes

<<<${1//$(tr "bdnpqu" "qpudbn"<<<$1|rev)}

Try it online!

Truthy/ambigram: returns empty string. Otherwise, the result is non-empty.

\$\endgroup\$

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