18
\$\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 vertical symmetry is considered (e.g. ({IH}) is not a valid submission, even though it has horizontal 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 catch is that you can't use a flag that allows you to reduce your code size by half. And it's a seriously hard language to pick up. :P
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Why sharp # disallowed? \$\endgroup\$ Commented Mar 26, 2018 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\$ Commented Mar 26, 2018 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\$ Commented Mar 26, 2018 at 3:32
  • \$\begingroup\$ Related. Related. \$\endgroup\$ Commented Mar 26, 2018 at 8:34

2 Answers 2

18
\$\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\$
7
  • \$\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\$ Commented Mar 26, 2018 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\$ Commented Mar 26, 2018 at 2:28
  • \$\begingroup\$ sorry, i missed that part. \$\endgroup\$ Commented Mar 26, 2018 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\$ Commented Mar 26, 2018 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\$ Commented Mar 26, 2018 at 3:00
5
\$\begingroup\$

Stax, 76 70 bytes

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

])}"_=_HM^^xW:&i={[+]==[+]}[)>^<(]({T)}|{(T})<(*]{[:!-_:>}<[<)*(>]>{<:_-!:]}[*)>b<+>d())(({[<++<]})&a=1&m=2" rel="nofollow noreferrer">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\$
2
  • \$\begingroup\$ The existence of R and W is really interesting. Program termination by pq combination is also impressive to me. \$\endgroup\$ Commented Mar 28, 2018 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\$ Commented Mar 28, 2018 at 8:03

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.