16
\$\begingroup\$

Background

Stack Cats is a reversible esoteric language made by Martin Ender. Each command in Stack Cats is either the inverse of itself (represented as a symmetric character, such as -_:T|), or has its inverse command (represented as the mirror image, such as () {} [] <>). Stack Cats has a strong syntactic requirement that the whole program should be the mirror image of itself. Note that this means any valid Stack Cats program is a natural mirror-image ambigram.

Here is the whole command set of Stack Cats:

  • Self-symmetric: !*+-:=ITX^_|
  • Symmetric pairs: () {} [] <> \/

Any other characters are invalid; any input having a character not in the character set above should output false.

The language has additional constraint that () and {} pairs must be always balanced, but for the sake of simplicity, you don't have to check for this condition.

The following are some examples of a valid Stack Cats program (again, note that you don't check for balanced parens):

{[+]==[+]}
[)>^<(]
({T)}|{(T})
<(*]{[:!-_:>}<[<)*(>]>{<:_-!:]}[*)>

These are not:

b<+>d
())(
({[<++<]})

Challenge

Write a program or function that determines if the given string is a valid Stack Cats program. Your code should also be a natural mirror-image ambigram, which means:

  • Your code should be a mirror image of itself.
    • Your code may have one or more newlines, as long as the whole code, displayed naturally, is a mirror image of itself.
    • You can omit or add trailing whitespaces on each line, since it doesn't change the display.
    • Tab characters are not allowed since they have some ambiguity on display.

Note: your code does not have to be a valid Stack Cats program; it may contain certain extra characters that are not allowed in Stack Cats. (See below for the complete list.)

For example, the following two programs are symmetric (and thus a valid submission), while the third is not:

({bTd})
[<q|p>]
({bTd})
  IXI
({bTd})
IXI
  • Regarding "mirror symmetry", only Stack Cats-style symmetry is considered (e.g. ({IH}) is not a valid submission, even though it has mirror symmetry).
  • Your code can only contain these sets of characters, plus newline:
    • Self-symmetric: space (0x20) + !"'*+-.8:=AHIMOTUVWXY^_ovwx|
    • Symmetric pairs: () /\ <> [] bd pq {}

The character set is chosen to be strictly symmetric or self-symmetric when displayed as code on SE.

Input and Output

The input range is any one-line string of printable ASCII characters.

You can choose to take input as a string, a list of chars, or a list of ASCII values.

You can choose to output either:

  • Any of the truthy/falsy values as defined by the language of your choice
    • The actual result values may differ between inputs (e.g. output 1 for a truthy input and 2 for another truthy one).
    • Swapping truthy and falsy values is not allowed.
  • Any two constant values for true/false respectively
    • In this case, the result values should exactly be one of the two constant values.

You should specify your input method and output values in your submission.

Winning Condition

This is , so lowest bytes in each language wins.

Notes

  • Standard loopholes are forbidden as usual.
  • Of course you can solve this in Stack Cats, but the chance is that you can't use a flag that allows to reduce your code size by half. And it's seriously hard language to pick up :P
\$\endgroup\$
  • 1
    \$\begingroup\$ Why sharp # disallowed? \$\endgroup\$ – tsh Mar 26 '18 at 3:05
  • 1
    \$\begingroup\$ @tsh It's slightly skewed in many fonts, including the code font on SE (at least it's what I see on Chrome). \$\endgroup\$ – Bubbler Mar 26 '18 at 3:19
  • \$\begingroup\$ @DLosc I tried to clarify some points around it. But if you think the description is still unclear, please feel free to edit. \$\endgroup\$ – Bubbler Mar 26 '18 at 3:32
  • \$\begingroup\$ Related. Related. \$\endgroup\$ – Martin Ender Mar 26 '18 at 8:34
16
\$\begingroup\$

JavaScript (ES6), 487 467 378 298 292 280 266 264 bytes

Saved 14 bytes thanks to @Bubbler

I=>(V=v=>!I[v]||((T=o=>[[]][+!!A[o]]||[(I[v]!=A[o]||A)[o^o<88/8]]+T(++o))(8-8)==I.pop())*V(++v))(V|(A='(){}[]<>\\/ !*+-:=ITX^_|'))//\\(('|_^XTI=:-+*! \//<>[]{}()'=A)|V)((v++)V*(()qoq.I==(8-8)((o++)T+[[8\88>o^o](A||[o]A=![v]I)]||[[o]A!!+][[]]<=o=T))||[v]I!<=v=V)<=I

Defines an anonymous function that takes an array of chars and returns the desired output. Output is truthy/falsy; usually 1/0, but the empty string gives true.

How?

The most obvious trick is to use //\\ as a center point to comment out the mirrored version of the code. After that, it becomes a game of figuring out the shortest way to solve the problem using only the charset given.

The first issue we run into is the lack of keywords and built-ins. We miraculously still have .pop(), but everything else will have to be done via the allowed operators (which includes a[b] and f(c)), with recursion to emulate loops.

The second issue is the lack of logical operators. Neither & and ? are allowed, which means the only decision-making operator we can use is ||. Therefore, we have to carefully structure our logic to account for this.

The first thing I did was to define a function T that mirrors an individual character. The basic idea is to loop through each character in a string of mirror-able chars, testing each for equality with the given char. If it is equal, we return its mirror—the char at index^1 for (){}[]<>\/, or the char itself for the rest.

The first problem I ran into here was getting either the mirrored char or a falsy value on each iteration. The solution I eventually came up with was (x!=A[o]||A)[o^o<88/8], where x is the input character, A is the mirroring alphabet, and o is the current index. If x is not the same as A[o], this gives true, and the index expression evaluates to undefined; otherwise, the ||A is activated, and we end up getting A[o^(o<11)].

The second problem is how to terminate the recursion. I found that the best way to do this is to simply concatenate the results of every iteration, returning the empty string when the end of A is reached. This presents us with two further problems: converting the undefineds to empty strings, and returning the empty string || something. These can be solved with array abuse: [a]+"" gives the string representation of a, or the empty string if a is undefined. As a bonus, [] is truthy but stringifies to the empty string, so we can conveniently use this as a "truthy empty string".

Now we can use the T function to mirror any single character. We do this recursively, comparing the mirror of I[v++] to I.pop() until the end of the array of chars is reached. We can't use && or & to check if all of the comparisons are truthy, but so use * instead. Multiplying all of these results together gives 1 if every character is the mirror of the one opposite, or 0 if any comparison fails.

And that is basically how this answer works. I probably didn't explain it very clearly, so please ask any questions you may have and point out any mistakes I have made.

\$\endgroup\$
  • \$\begingroup\$ U=([A,...H])=>!(V=H.pop())||!(W=([x,...X]=(T="!*+-:=ITX^_|")+"(){}[]<>\\/",[o,...O]=T+")(}{][></\\")=>!x||((o!=A)+(x!=V))*(W(X,O)))()*U(H)//... 280 bytes \$\endgroup\$ – tsh Mar 26 '18 at 2:22
  • \$\begingroup\$ @tsh commas are not allowed in the source code, as they are not symmetrical (in the SE code font) and have no mirror (in ASCII, anyway) \$\endgroup\$ – ETHproductions Mar 26 '18 at 2:28
  • \$\begingroup\$ sorry, i missed that part. \$\endgroup\$ – tsh Mar 26 '18 at 2:41
  • \$\begingroup\$ @tsh I missed it too at first, and spent 20 minutes on a solution only to realize it couldn't be valid :P \$\endgroup\$ – ETHproductions Mar 26 '18 at 2:42
  • \$\begingroup\$ Anyway, since you had already posted a JavaScript solution. We do not need another JSF*k solution now... // If I were you, I would fix this by just compile it to JSF*k... \$\endgroup\$ – tsh Mar 26 '18 at 3:00
1
\$\begingroup\$

Stax, 76 70 bytes

:Wx^^MH_=_"{([</!*+-:=ITX^_|":W-!*pq*!-W:"|_^XTI=:-+*!\>])}"_=_HM^^xW:

Run and debug it

Stax is friend of Stack Cats and has internals to generate the later half of a Stack Cats program from the first half. If we don't care about the restriction on the source and don't need to check the charset, here is a 4-byte solution:

4 bytes

:R_=

Run and debug it

Explanation

:Wx^^MH_=_"{([</!*+-:=ITX^_|":W-!*pq...
:W                                         "Mirror" the string
                                           Equivalent to appending the reverse of the string to itself
                                           And map `{([</\>])}` to its mirror in the appended string
  x^^                                      2, but we can't just use `2` here ...
     MH                                    Partition the "mirror"ed string to two parts, take the later part.
       _=                                  The string is the same as the original one (*)
                                           `:Wx^^MH_=` is just `:R_=`, but we can't use `R` here ...
         _                                 Input string
          "{([</!*+-:=ITX^_|":W-           Remove valid characters from input
                                !          The final string is empty (**)
                                 *         (*) and (**)
                                  p        Pop and print result
                                   q       Peek stack and print
                                           Since the stack is now empty, this causes the program to terminate
                                    ...    Not executed
\$\endgroup\$
  • \$\begingroup\$ The existence of R and W is really interesting. Program termination by pq combination is also impressive to me. \$\endgroup\$ – Bubbler Mar 28 '18 at 7:22
  • \$\begingroup\$ Thank you. The instructions are actually two bytes: :R and :W. I feel that I can't help telling everyone there are internals in Stax that do this. \$\endgroup\$ – Weijun Zhou Mar 28 '18 at 8:03

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.