# Official Tetris Piece Ordering

Classic Tetris was often a difficult game to play. One problem was that you could only work with the Tetris piece you were given, so if you had an awkward board, it would sometimes be difficult to find a suitable location to put your piece. Another problem was that you could only look one piece ahead, so it was difficult to prepare your board for upcoming pieces. These problems together made the classic game quite frustrating to play.

Official Tetris solves these problems with the hold slot and next queue. The next queue allowed a player to look ahead 5 pieces, instead of 1. However, if a player was still unsatisfied with the piece they currently had, they could "hold" the piece in the hold slot and instead use the next piece in the queue.

For example, take the board below.

It would be hard to place the square piece on the board, so the player could "hold" the piece and use the jagged 'Z' piece instead:

Once you "hold" a piece, however, the game doesn't allow you to keep "holding", or switching the pieces again and again until you've used the new piece. For instance, in the example above, you wouldn't be able to switch back to the square piece until you've used the jagged 'Z' piece.

If you want to try this mechanic out yourself, go to JSTris or tetr.io and play around with it.

# Challenge:

The pieces will be labelled J, L, S, Z, O, I, or T. Given the next queue, the current piece, and the hold piece (assume it exists for now), generate all possible orders you can place 6 of the pieces in.

For example, if the next queue was [Z,S,L,J,I], the current piece was O, and the held piece was T, 3 possible orders would be the following:

1. O, Z, S, L, J, I (no hold used at all)
2. T, Z, S, L, J, I
^ hold O here
3. O, Z, T,        L, J, S
^ hold S here   ^ hold I here, S was in hold slot so put S here

• I know this is similar to this, but this challenge generates a list of all possible sequences instead of finding whether one sequence is possible. Also, to my knowledge, that question doesn't consider the hold function.
– vxpr
Dec 29, 2023 at 4:34
• May I assume the 6 pieces are different or will there be any duplicate ones?
– tsh
Dec 29, 2023 at 4:44
• *7 different pieces?
– Tbw
Dec 29, 2023 at 4:55
• @tsh No, it's possible for there to be duplicate pieces, but it's fine if in your solution that there are duplicate solutions.
– vxpr
Dec 29, 2023 at 4:55
• @Tbw Oh. Sure, you can put them in a single array.
– vxpr
Dec 29, 2023 at 5:48

# Python 3, 70 bytes

f=lambda x,y=[]:x[1:]and f(x[1:],y+x[:1])+f(x[:1]+x[2:],y+[x[1]])or[y]


Try it online!

# Python 3, 66 bytes by tsh

f=lambda a,o='':a[1:]and f(a[1:],o+a[0])+f(a[0]+a[2:],o+a[1])or[o]


Try it online!

I'm not sure if I understood correctly,

• 66: f=lambda a,o='':a[1:]and f(a[1:],o+a[0])+f(a[0]+a[2:],o+a[1])or[o], input str, output List[str]
– tsh
Dec 29, 2023 at 6:12

# JavaScript (ES6), 49 bytes

Expects (queue, held_piece), where the first item in the queue is the current piece.

f=([c,...a],h,o="")=>c?f(a,h,o+c)+[,f(a,c,o+h)]:o


Try it online!

# Curry (PAKCS), 39 bytes

f(c:q)h=h:f q c
f(c:q)h=c:f q h
f""h=""


Try it online!

Takes the queue (starting from the current piece) as a string and piece in hold as a char. Basically, the code just describes what can happen during a move. Due to non-deterministic behavior, Curry just walks through all the possibilities, which is exactly what we need here.

# Charcoal, 32 29 bytes

⊞υＳＦＳ≔ΣＥυ⟦⭆κ⁺μ×ι¬ν⁺ικ⟧υＥυ⮌✂ι¹


Attempt This Online! Link is to verbose version of code. Takes the hold piece as the first input and the current piece and next queue as a single string as the second input. Explanation:

⊞υＳ


ＦＳ


Loop over the queue.

≔ΣＥυ⟦⭆κ⁺μ×ι¬ν⁺ικ⟧υＥυ


For each position, create two positions, one playing the current piece and one holding it and playing the hold piece. Note that the piece is played at the start of the string, so the played pieces are currently in reverse order.

⮌✂ι¹


Print the played pieces of the positions in the correct order.

# Python 3, 69 bytes

f=lambda x,y,*a:a and[p+j for p,q in[x+y,y+x]for j in f(q,*a)]or[x,y]


Try it online!

Input 7 chars, output str.

# Retina 0.8.2, 30 bytes

+%(.),(.)
$2$1,$'¶$$1$2,
.,



Try it online! Takes the hold piece as the first input and the current piece and next queue as a single string as the second input, separated by a comma. Explanation:

(.),(.)
$2$1,$'¶$$1$2,


Match the hold piece and the current piece and create two new positions, one by playing the current piece and one by playing the hold piece.

+%


Repeat the above separately on each position until queue is empty.

.,



Delete the hold piece and separator from each result.

# Jelly, 19 bytes

W;Ɱ2ṗ6¤œ-ị@¥\€œ-Ɲ€Y


Try it online!

A full program that takes a string as its argument (held, current, five next) and prints the 64 possibilities to STDOUT.

## Explanation

W                   | Wrap in a list
;Ɱ                 | Concatenate to each of:
2ṗ6¤             | - 2 Cartesian power 6 (i.e. all lists length 6 of 1s and 2s)
¥\€      | For each of these, reduce using the following, collecting up intermediate results:
œ-ị@         | - Set difference between the current set of pieces and the one found at the index of the right argument (either the first or second)
œ-Ɲ€  | Set difference of neighbours for each of these outputs
Y | Join with new lines


# Scala, 127 bytes

A port of @l4m2's Python answer in Scala.

127 bytes, it can be golfed more.

Golfed version. Attempt This Online!

type L[C]=List[C]
def f[C<:Char](x:L[C],y:L[C]=Nil):L[L[C]]=x match{case h::n::t=>f(n::t,y:+h):::f(h::t,y:+n);case _=>List(y);}


Ungolfed version. Attempt This Online!

object Main {
def f(x: List[Char], y: List[Char] = List()): List[List[Char]] = x match {
case head :: next :: tail => f(next :: tail, y :+ head) ::: f(head :: tail, y :+ next)
case _ => List(y)
}

def main(args: Array[String]): Unit = {
val result = f(List('O', 'T', 'Z', 'S', 'L', 'J', 'I'))
println(result)
}
}


# APL(Dyalog Unicode), 33,32,31,29,26 bytes SBCS

(,⍳6⍴2){1↓¯1∘⌽@(⍸1,⍺)⊢⍵}¨⊂


Try it on APLgolf!

A tacit function which takes a string on the right in the order of held piece, current piece, queue.

## Old Explanation (essentially the same)

(⍳64){1↓¯1∘⌽@(⍸~(7⍴2)⊤⍺)⊢⍵}¨⊂­⁡​‎‎⁪⁡⁪⁠⁪⁢⁤⁡⁪‏‏​⁡⁠⁡‌⁢​‎‎⁪⁡⁪⁠⁪⁢⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁣⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁣⁤⁪‏‏​⁡⁠⁡‌⁣​‎‎⁪⁡⁪⁠⁪⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁤⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁣⁪‏‏​⁡⁠⁡‌⁤​‎⁪⁪⁠‎⁪⁡⁪⁠⁪⁢⁡⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁤⁪‏⁠⁪⁪‏​⁡⁠⁡‌⁢⁡​‎‎⁪⁡⁪⁠⁪⁢⁡⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁤⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁢⁪‏‏​⁡⁠⁡‌⁢⁢​‎‎⁪⁡⁪⁠⁪⁤⁤⁪‏‏​⁡⁠⁡‌⁢⁣​‎‎⁪⁡⁪⁠⁪⁤⁣⁪‏‏​⁡⁠⁡‌⁢⁤​‎‎⁪⁡⁪⁠⁪⁤⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁤⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁤⁤⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁡⁤⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁢⁤⁪‏‏​⁡⁠⁡‌⁣⁡​‎‎⁪⁡⁪⁠⁪⁤⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁣⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁣⁢⁪‏‏​⁡⁠⁡‌⁣⁢​‎‎⁪⁡⁪⁠⁪⁣⁡⁪‏⁠‎⁪⁡⁪⁠⁪⁣⁢⁪‏⁠‎⁪⁡⁪⁠⁪⁣⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁣⁤⁪‏‏​⁡⁠⁡‌⁣⁣​‎‎⁪⁡⁪⁠⁪⁢⁣⁪‏⁠‎⁪⁡⁪⁠⁪⁢⁤⁪‏‏​⁡⁠⁡‌­
⊂  # ‎⁡put the input in an array
# ‎⁡    so the 'each' doesn't apply to it
{                    }¨   # ‎⁢apply this dfn for each...
(⍳64)                 ⍺        # ‎⁣    number between 0 and 63
7⍴2           # ‎⁤2 2 2 2 2 2 2 so we can...
(7⍴2)⊤         #     convert 7 binary digits
~               # ‎⁢⁢bitwise not
⍸                # ‎⁢⁣indices of 1s
(⍸~(7⍴2)⊤⍺)       # ‎⁢⁤all combinations of 0-6 that have a 0
@           ⊢⍵     # ‎⁣⁡at those indices of the input...
¯1∘⌽                   # ‎⁣⁢    shift the letters left by 1
1↓                       # ‎⁣⁣then drop the first letter
💎


Created with the help of Luminespire.

This works without recursion because we can take the composition of swaps (holding a piece) and get $$(0~d)(0~c)(0~b)(0~a) = (0~a~b~c~d)$$ in permutation notation. So we take all possible selections of places to hold, add a 0 to each, and permute the input using ¯1∘⌽@, rotating the selected indices by 1.

# x86-64 machine code, 23 bytes

B6 40 56 88 F2 A8 AA AC D0 EA 73 FA A4 75 F9 5E FE C6 71 EE 88 17 C3


Try it online!

Following the standard calling convention for Unix-like systems (from the System V AMD64 ABI), this takes:

• in RDI, an address at which to place the result, as multiple null-terminated byte strings run together, terminated with an empty string;
• in RSI, the address of the input, as a null-terminated byte string.

In assembly:

f:
mov dh, 64  # Set DH to 64.
# (This value will run from 64 to 127. The bottom six bits indicate
#  when to switch the hold piece in, covering all possibilities.
#  The top two bits are always 01.)
repeat:
push rsi    # Save the value of RSI onto the stack.
mov dl, dh  # Set DL to the value of DH.
.byte 0xA8  # Absorbs the next instruction into "test al, 0xAA".
switch:
stosb       # Write AL to the output string, advancing the pointer.
lodsb       # Read from the input string into AL, advancing the pointer.
nextpiece:      # (AL will store the currently held piece.)
shr dl, 1   # Shift DL right by 1 bit. The bit shifted out goes into CF.
jnc switch  # Jump if CF=0.
movsb       # Add a byte from the input string to the output string,