Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Challenges

Comments on Label a hinged tetromino

Parent

Label a hinged tetromino

+3
−0

Given a hinged tetromino, give it a unique, consistent label independent of location, rotation, and reflection.

Tetrominoes

A tetromino is a connected subset of the square tiling, composed of 4 squares, where connection can only be edge connection.

Here are the 5 tetrominoes:

the 5 tetrominoes

Hinged tetrominoes

A hinged tetromino is a connected subset of the square tiling, composed of 4 squares, where connection can be edge connection or corner connection.

Here are the 22 hinged tetrominoes:

the 22 hinged tetrominoes

Equivalence

The following 8 shapes are all considered to be the same hinged tetromino. They are equivalent because any one can be reached from any other by some combination of rotation, reflection, and translation:

8 equivalent hinged tetrominoes

All 8 of these rotated and reflected versions must have the same label. Translating, rotating, or reflecting a hinged tetromino must not change its label.

Input

  • A 4 by 4 grid containing a hinged tetromino.
  • Apart from the 4 squares of the hinged tetromino, all other squares are empty.
  • You may take input in any convenient format that does not include full or partial precalculation. For example:
    • A string or sequence of strings.
    • A 1 or 2 dimensional list/array/vector.
    • A sequence of coordinates (even a sequence of X coordinates and a separate sequence of Y coordinates if you wish).
    • A 16 bit variable where each 1 bit represents part of the hinged tetromino and each 0 bit represents part of the background.
    • A variable with more than 16 bits where only 16 of the bits are used would also be acceptable.

Output

  • A label for the hinged tetromino.
  • The label for each hinged tetromino can be anything you choose, including nothing (no output).
  • Each label must be unique (it must not be the label for any other hinged tetromino).
  • Each label must be consistent (a given hinged tetromino must always result in the same label, regardless of rotation, reflection, or translation).

Scoring

This is a code golf challenge. Your score is the number of bytes in your code.

Explanations are optional, but I'm more likely to upvote answers that have one.

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

2 comment threads

Question about inputs (8 comments)
Clarification on rotations, etc. (2 comments)
Post
+1
−0

JavaScript (Node.js), 892 bytes (not fully working)

I=>{N=a=>Math.min(...a);X=a=>Math.max(...a)
C=[];s=[t=[],[]];S="splice";p="push"
c=I[P="map"](a=>a[P](p=>p-N(a)))
d=X(c[1])-X(c[r=0])
d>0?r=1:d||c[1][P]((a,b)=>a-1||C[p](c[i=0][b]))
if(''+c[0].filter((a,b)=>C[O="indexOf"](a)>=0&&c[1][b]!=1)){r=1,C=[],c[0][P]((a,b)=>a==1&&C[p](c[1][b]))
if(''+c[1].filter((a,b)=>C[O](a)>=0&&c[0][b]!=1)&&X(c[0])==2)
return c[1][P]((a,b)=>a-1?c[0][b]!=1?1:0:c[0][b]!=1?0:2).reduce((a,b)=>a+b)}
if(r)while(''+c[0]){if(c[0][i]==N(c[0]))c[P]((a,b)=>{s[1-b].push(a[i]),c[b][S](i,1)}),i=-1;i++}''+s[0]?0:s=c
m=X([1111*X(s[1])-s[1][R="reverse"]()[J="join"]``,+s[1][R]()[J]``])
u=(''+s[0]).split`,`[P](a=>X(s[0])-a)
v=(''+s[1]).split`,`[P](a=>+a)
w=[]
if(m-s[1][J]``)while(''+s[1]){if(s[1][i]==X(s[1]))t[p](s[0][i]),s[P](a=>a[S](i,1)),i=-1;i++}i=3
while(''+u){if(v[i]==X(v))w[p](u[i]),u[S](i,1),v[S](i,1),i=v.length;i--}''+t?0:t=s[0]
m*=X([+t[J]``,+w[J]``]);return m}

Attempt This Online! (Add input in the footer)

I can confirm that solving something hard, even if it took 6 million years, is very rewarding :) (Though the code's REALLY long)

Definitely can still be optimized, there's no way 892 bytes is the limit.

How does it work? (Assuming it works, which it doesn't fully yet)

Since symmetry is important, my first thought involved finding the average X- and Y-coordinates of the squares. It works, but sadly the labels occur more than once (meaning they're not unique).

After days of brainstorming on how to make them unique which almost drove me insane, I came to the conclusion we need a better method.

Ah! Here's the better method.

  • Let's say the top left grid is [0,0], the X-axis goes left to right, and the Y-axis up to down.
  • We separate the X- and Y-coordinates, and mush them each together to form 2 numbers.
  • To ensure uniqueness, we then multiply those numbers and assign it as the label. Yeay!

For a better picture, let's label the coordinates of the 10th-13th tetrominoes: Tetrominoes 10-13 We get

[[0,0], [1,0], [3,0], [2,1]],
[[0,0], [2,0], [1,1], [3,1]],
[[0,0], [3,0], [1,1], [2,1]],
[[0,0], [1,0], [2,1], [3,1]].

(Note that the order of the coordinates is just like reading text in most languages (top to bottom, left to right))

Now, mushing the X- and Y-coordinates would look like this:

X: [0,1,3,2] Y: [0,0,0,1]
X: [0,2,1,3] Y: [0,0,1,1]
X: [0,3,1,2] Y: [0,0,1,1]
X: [0,1,2,3] Y: [0,0,1,1]

(Also note that order really, really matters here)

Anyway, when we flip the tetrominoes upside-down: Flipped tetrominoes 10-13 The coordinates become

[[2,0], [0,1], [1,1], [3,1]],
[[1,0], [3,0], [0,1], [2,1]],
[[1,0], [2,0], [0,1], [3,1]],
[[2,0], [3,0], [0,1], [1,1]].

What did we achieve? As it turns out, we can flip a tetromino by flipping its Y-coordinates and then rearranging them!

(In this example, we flip the Y-coordinates from 0 to 1 and vice versa. The idea is pretty similar for other tetrominoes.)

Also, notice that the Y-coordinates are always in ascending order, which is what we'll need to do when rearranging.

The same is true when flipping the tetrominoes horizontally: flip the X-coordinates, and rearrange. (Feel free to check this by hand)

Anyway, this means we can get the flipped version of any tetromino. Progress!

What to do next?

  • From those flipped tetrominoes, get their mushed together X- and Y-coordinates.
  • Compare them to the original tetromino. Use the one with the greatest value.
    (That means if we have [0,1,3,2] and [2,0,1,3], we pick the latter because 132 < 2013. So, we get the same result even if we flip the tetrominoes around!)

Rotating

So far we've solved mirroring (or flipping), but what about the pesky rotation? How do we rotate, and how do we know when to rotate?

My idea is that if a tetromino has two squares side-by-side or is more tall than wide, we rotate. Otherwise, we don't.

For not square-ish tetrominoes like 10-13 (the ones I showed you before), this works well!

However, how about the square-ish ones where its width is the same as its height?

Let's take a look at this guy, the 16th tetromino: Yancy Let's call it Yancy. Why not?

...anyway, Yancy is a square-ish 3x3 tetromino, and these are quite annoying (no offense, Yancy).

Yancy here does not have two squares side-by-side. This means we shouldn't rotate it, and it's fine just the way it is :)

However, take Yancy's cousin Yandy (who's quite odd): Yandy Since Yandy has two squares side-by-side, we need to rotate it. The question is how?

Well... after some pondering, here's what I figured out:

  • We can think of rotation as a diagonal mirror, something like this: Yandy mirror (Note that even if the result is flipped, that's still the same tetromino which means it doesn't matter!)
  • When we mirror something (like in the image), its X-coordinates become its Y-coordinates and vice versa.
  • The only thing left to do is to rearrange them, and we're done!

Now, only a bunch of tetrominoes will always have two blocks side-by-side even when rotated, and they're all symmetric so they definitely won't cause any problems.

Oh wait...

Actually, they do. Thankfully enough, there's an unelegant "hardcoded" way to give them unique labels (see line 6-8 in my code) and I don't have to spend another 6 million years trying to fix that 🙂

After all this, though, I can only hope my effort isn't ruined by a tetromino which I haven't tested on and somehow breaks the code. Feel free to test the code yourself.

Update: I tested every possible transformation, and there were errors in tetrominoes 4-9, 11, 13, 21, and 22. Sigh.

Also, the code's really golfed that not even I can immediately understand it. That's why I didn't bother trying to break down the code :)

History
Why does this post require moderator attention?
You might want to add some details to your flag.

1 comment thread

Tested - looks good (5 comments)
Tested - looks good
trichoplax‭ wrote 10 months ago

I love the detailed explanation!

I couldn't tell from the explanation whether this would still work with the shapes at different positions in the 4 by 4 input area. I adjusted some of the test cases by translating them down or to the right by adding newlines or spaces and they still work.

I also tested the separateXY helper function, that converts from text input to an array of X coordinates and an array of Y coordinates (which is explicitly allowed in the input section of the challenge). I wanted to see whether this gave the same result for a shape shifted down or right, which would count as precalculation. I can confirm that it does not precalculate - it correctly gives different coordinates if extra newlines or spaces are used to move the shape, and these different coordinates are then correctly given the same label.

TheCodidacter, or rather ACodidacter‭ wrote 10 months ago · edited 10 months ago

trichoplax‭ thanks! You're right, I did implement translation but forgot to mention it in the post. It was way easier than the two other transformations though (which took 6 million years).

Also, just curious... you've solved the challenge yourself, right?

Just tested the code on every possible transformation, and it incorrectly gives labels to some transformations of tetrominoes 4-9, 11, 13, 21, and 22. Wow.

Hopefully I don't take another million years to fix this :)

trichoplax‭ wrote 10 months ago

Glad your testing was more thorough than mine... I should probably generate the exhaustive list of test cases for testing future entries.

trichoplax‭ wrote 10 months ago

I usually try to make a reference implementation before posting a challenge, but in this case it only exists in my head (which means it's untested). At some point I'll implement it in real code and post it as an answer. It uses a more generic approach which I'm not sure will be golfy (I just needed to know there was a possible solution before posting the challenge).