For the purpose of this challenge a rectangular piece of ASCII art is Mondrian if it looks the same upside down.

What does "looks the same" mean?

A feature is any orthogonally connected region of at last 5 characters. A Mondrian feature is any feature that appears in the same picture upside down. (This includes the possibility of the feature being rotationally symmetric in which case the feature counts as its own upside-down appearance.)

A picture is Mondrian if each of its characters is part of at least one Mondrian feature.

Your task:

Write a function or program that given a rectangle of ASCII characters returns it upside down if it is Mondrian and unchanged otherwise. If the entire picture is rotationally symmetric (such that it is Mondrian but has no upside-down state) the program or function should die horribly. You are free to choose amongst crash, hang, run forever, error out or simply end without output.

You may assume that the input is a clean rectangle of size at least 2x2.

This is code-golf. Standard rules apply, except despite this more or less being a binary decision you cannot do the usual truthy / falsy shenanigans.

You can take input in any reasonable format, e.g. newline separated string, list of lines, array of characters, but must output in the same format.

More clarifications on "upside down":

  • We mean (180-degree) rotation, not mirroring.

  • Characters are atoms, for example p upside down is p, not d.

Also: Features are allowed to overlap. (They do in both Mondrian examples below.)



 |     |


Explanation (possible list of Mondrian features) for 2nd example:





Not Mondrian:

./ \...

Mondrian impostor (not Mondrian):


Die horribly:

/ \
\ /
  • 5
    \$\begingroup\$ I am struggling to understand the rule "A Mondrian feature is any feature that appears in the same picture upside down" even with the test cases. Eg, I don't see why the 2nd truthy example is Mondrian, or why the imposter is not Mondrian -- the entire middle section appears the same upside down, and is larger than 5 chars.... \$\endgroup\$
    – Jonah
    Oct 30, 2022 at 4:18
  • 2
    \$\begingroup\$ I've closed this as unclear to temporarily stop potentially invalid answers. It is not clear whether features can overlap and the entire phrasing of this is very disjointed in a way that makes it hard to understand. \$\endgroup\$
    – Wheat Wizard
    Oct 30, 2022 at 18:40
  • 2
    \$\begingroup\$ Fwiw, it took me a while but once I got it, it's pretty simple. Take every possible subrectangle of the input, and for each, rotate it 180 in place and mark every invariant element as a 1 iff it is part of an orthogonally connected block of other invariant elements at least 5 big. After doing that for every subrectangle, including the original input itself, has every element been marked a 1 at some point? If so, the input is Mondrian. Then add on the "die" as a special case. \$\endgroup\$
    – Jonah
    Oct 30, 2022 at 18:50
  • 1
    \$\begingroup\$ @GrainGhost I think it's clear that features can overlap, since the question says that "each of its characters is part of at least one Mondrian feature". I agree about the wording being rather hard to understand \$\endgroup\$ Oct 30, 2022 at 19:37
  • 4
    \$\begingroup\$ I politely disagree. To me the challenge description is actually crisp and to the point. It introduces the relevant concepts of a feature and a Mondrian feature and then uses a simple for-all rule to define global Mondrianness. Very straight-forward. It may be hard to understand if you are not used to this kind of logical construction but I'm at a loss as to how anyone could describe it as "disjointed". \$\endgroup\$ Oct 31, 2022 at 13:36

2 Answers 2


J, 114 bytes

-.@(-:g)#,=&#[:~.@;@,(g=.|.@|:^:2)([:(#~4<#&>)@(</.~[:+./ .*^:_~1>:|@-/~)$j./@#:I.@,)@:="2]|.!.(u:0)~[:>@,@{<@i:@$

Try it online!

The basic approach is the only mildly interesting part, so I will explain that without breaking down the J mechanics, which are straightforward, could likely be golfed further, though it wouldn't be terribly interesting without a high-level strategy change as well.

  1. Do the 180 rotation.
  2. Check where it is equal to the input.
  3. Also check every possible 2d sliding of the input. You can imagine it as two sheets of paper, keeping the edges aligned, sliding the top one in every direction where there is still any overlap.
  4. For each of these, we then have a boolean matrix showing which elements are equal. But we only care about connected 1 islands of length 5 or more.
  5. To find these, we turn the 1s into coordinates and compute an adjacency matrix, taking their transitive closure, grouping by equal rows, and only keeping "sets" of equal rows of length 5 or more. This is part is done here.
  6. We combine all these valid coordinates we've found for every slid position, take their uniq, and see if it has as many elements as the input. If so, we are Mondrian.
  7. Finally, we check if the rotation is equal to the input directly, and filter away our input if it is.

Python3, 730 bytes:

v=lambda x:[[i[j]for i in x[::-1]]for j,_ in E(x[0])]
t=lambda x:{(x,y):k for x,a in E(x)for y,k in E(a)}
V=lambda b,x,y:0<=x<len(b)and 0<=y<len(b[0])
P=lambda x:M(W[max(i for i,_ in E(x[0])if all(y[:i].lstrip()==''for y in x)):].strip()for W in x)
def f(b):
 q,r=[(i,I,[i])for i in B for I in C if B[i]==C[I]],[]
 while q:
  if len(p)>4:r+=[p]
  for j,k in[(1,0),(0,1),(-1,0),(0,-1)]:
   if V(b,J:=x+j,K:=y+k)and V(c,Q:=X+j,W:=Y+k)and(J,K)not in p and B[(J,K)]==C[(Q,W)]:q+=[((J,K),(Q,W),p+[(J,K)])]
 if{B[j]for k in r for j in k}=={j for k in b for j in k}:
  if P([*map(F,b)])==P([*map(F,c)]):f(b)
  return M(map(F,v(v(b))))
 return M(map(F,b))

Try it online!

  • \$\begingroup\$ You can store '\n'.join and ''.join in variables to save 24 bytes (no TIO link because it's top long) \$\endgroup\$ Oct 30, 2022 at 18:51
  • \$\begingroup\$ @ Thanks, updated \$\endgroup\$
    – Ajax1234
    Oct 31, 2022 at 1:32

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