Đưa ra một danh sách các động thái Tetris, trả về số lượng dòng hoàn thành


37

Sự miêu tả

Chúng tôi xem xét một phiên bản Tetris hơi đơn giản hóa trong đó mỗi lần di chuyển bao gồm:

  • xoay mảnh theo chiều kim đồng hồ, 0 đến 3 lần
  • định vị mảnh tại một cột nhất định
  • giảm nhanh

Mục tiêu là để xác định số lượng dòng hoàn thành, đưa ra một danh sách các động thái Tetris như vậy.

Các hàng đã hoàn thành được loại bỏ khi các mảnh được thả, theo các quy tắc Tetris tiêu chuẩn.

Sân chơi

Sân chơi rộng 10 cột. Không có Game Over và người ta cho rằng luôn có đủ không gian và thời gian để thực hiện các hành động trên, bất kể cấu hình của sân chơi. Chiều cao của sân chơi không thực sự quan trọng ở đây, nhưng bạn có thể sử dụng 22 hàng tiêu chuẩn làm giới hạn trên.

Hình dạng của Tetrominoes

hình dạng

Đầu ra đầu vào

Đầu vào

Một danh sách các dấu di chuyển Tetris được phân tách bằng dấu phẩy được mã hóa với 3 ký tự. Hai ký tự đầu tiên mô tả hình dạng Tetromino sẽ sử dụng và ký tự cuối cùng mô tả vị trí mà nó bị rơi.

  1. Tetromino: I, O, T, L, J, Zhoặc S, theo thứ tự như trên.
  2. Số vòng quay theo chiều kim đồng hồ: 0đến3
  3. Cột: 0đến 9. Đây là cột trong đó góc trên cùng bên trái của mảnh (được đánh dấu bằng một xhình trên) nằm sau vòng xoay 1

Giả định rằng tất cả các di chuyển trong danh sách được cung cấp là hợp lệ. Không cần kiểm tra các mục không hợp lệ, chẳng hạn như I07( Ihình ngang đặt quá xa bên phải).

1 Bạn có thể tự do thực hiện thuật toán xoay thực tế hoặc mã hóa tất cả các hình dạng khác nhau, miễn xlà vị trí đó nằm trong cột được cung cấp bởi ký tự thứ ba của di chuyển.

Đầu ra

Số dòng hoàn thành.

Thí dụ

thí dụ

O00,T24sẽ tạo vị trí đầu tiên và O00,T24,S02,T01,L00,Z03,O07,L06,I05sẽ tạo vị trí thứ hai.

Do đó, chuỗi sau sẽ tạo Tetris và sẽ trả về 4:

O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19

Các trường hợp thử nghiệm

1) "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" -> 4
2) "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" -> 5
3) "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" -> 4
4) "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,
    I10,I10" -> 8
5) "O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02" -> 8

Trang thử nghiệm

Bạn có thể sử dụng JSFiddle này để kiểm tra danh sách di chuyển.


1
Những mảnh được xoay về trục nào?

1
@Arnauld Tôi khuyên bạn nên xem hệ thống siêu xoay và chỉnh sửa hình ảnh một chút. tetris.wikia.com/wiki/SRS

1
Vì vậy, chúng ta có thể coi chúng là 25 (15 nếu bạn không đếm các bản sao) hình dạng khác nhau, sau đó?

1
Các giải pháp có thể lấy đầu vào dưới dạng một mảng thay vì một chuỗi được phân tách bằng dấu phẩy không?
Jordan

1
Đây là câu hỏi PCG tốt nhất mà tôi đã thấy trong một thời gian dài. Thật là một ý tưởng hay! Tốt nhất theo nghĩa chủ quan của thú vị và thực tế và không quá lớn nhưng không quá nhỏ.
GreenAsJade

Câu trả lời:


5

PHP, 405 399 378 372 368 360 354 347 331 330 328 319 309 300 byte

(với ánh xạ khối Dave Daves )

<?$f=[~0];L:for($y=0;$f[++$d+$y]|$s;$s>>=4)$y-=1022<$f[$d+$y]=$f[$d]|$s%16<<$x;$c-=$y;if(list($p,$r,$x)=$argv[++$z])for($s=I==$p?$r&1?4369:15:hexdec(decoct(O==$p?27:ord(base64_decode('M1ozWjqaF1kemR6ZPJMPyTnSJ0s')[ord($p)/4%5*4+$r])));;$d--)for($y=4;$y--;)if ($f[$d+$y]>>$x&$s>>$y*4&15)goto L;echo$c;

chương trình, di chuyển như các đối số riêng biệt, in kết quả

phân tích chức năng:

di chuyển như mảng, trả về kết quả

function t($a)
{
    $f=[~$z=0];             // init field $f (no need to init $z in golfed version)
    L:                      // jump label
                            // A: paint previous piece at line $d+1:
#   if($s)paint($f,$s,$d+1,$x);
    for($y=0;               // $y = working line offset and temporary score
        $f[++$d-$y]|$s;$s>>=4)// next field line; while field or piece have pixels ...
        $s>>=4)                 // next piece line
        $y+=1022<               // 3: decrease temp counter if updated line is full
            $f[$d-$y]=$f[$d]    // 2: use $y to copy over dropped lines
                |$s%16<<$x;     // 1: set pixels in working line
    $c+=$y;                         // add temp score to global score
#   paint($f);var_dump($c);
    if(list($p,$r,$x)=$a[$z++])// B: next piece: $p=name; $r=rotation, $x=column
#   {echo"<b>$z:$p$r-$x</b><br>";
        for(                // C: create base 16 value:
            $s=I==$p
                ? $r&1?4369:15  // I shape (rotated:0x1111, not rotated:0xf)
                : hexdec(decoct(    // 5: convert from base 8 to base 16
                    O==$p ? 27  // O shape (rotation irrelevant: 033)
                    : ord(          // 4: cast char to byte
                                    // 0: 4 bytes for each remaining tetromino
                        base64_decode('M1ozWjqaF1kemR6ZPJMPyTnSJ0s')[
                            ord($p)/4%5 // 1: map STZJL to 01234
                            *4      // 2: tetromino offset
                            +$r]    // 3: rotation offset
            )));;$d--
        )
            for($y=4;$y--;) // D: loop $y through piece lines
                if ($f[$d+$y]>>$x & $s>>$y*4 & 15) // if collision ...
                    goto L;         // goto Label: paint piece, tetris, next piece
#   }
    return$c;               // return score
}

để tham khảo: ánh xạ cũ

            hexdec(decoct(          // 3. map from base 8 to base 16
                                    // 0. dword values - from high to low: rotation 3,2,1,0
                [0x991e991e,0xc90f933c,0x4b27d239,0x1b1b1b1b,0,0x5a335a33,0x59179a3a]
                [ord($m[0])/2%9]    // 1. map ZJLO.ST to 0123.56 -> fetch wanted tetromino
                >>8*$m[1]&255       // 2. the $m[1]th byte -> rotated piece
            ))

thử nghiệm

xem câu trả lời PHP khác của tôi

muốn xem?

xóa nguồn #khỏi hàm và thêm phần này:

function paint($f,$s=0,$d=0,$x=0){echo'<pre>';for($y=count($f)+5;$y--;$g=0)if(($t=(($z=$y-$d)==$z&3)*($s>>4*$z&15)<<$x)|$r=$f[$y]|0){$z=$t?" $r|$t=<b".(1022<($z=$t|$r)?' style=color:green':'').">$z":" <b>$r";for($b=1;$b<513;$b*=2)$z=($t&$b?'<b>'.($r&$b?2:1).'</b>':($r&$b?1:'.')).$z;printf("%02d $z</b>\n",$y);}echo'</pre>';}

một số bước chơi gôn

Rev. 5: Một bước nhảy vọt lớn (399- 21 = 378) đến bằng cách di chuyển cột dịch chuyển
từ một vòng lặp riêng sang hai vòng lặp hiện có.

Rev. 8: Chuyển từ mảng sang cơ sở 16 cho mảnh ($ s) không mang lại nhiều,
nhưng nhường chỗ cho một số môn đánh gôn hơn.

Rev. 17: bẻ khóa các giá trị với base64_encode(pack('V*',<values>))
và sử dụng lập chỉ mục byte thay vì unpackđể lưu 16 byte

Rev. 25 đến 29: lấy cảm hứng từ mã Dave Daves: băm mới (-2), thiết kế vòng lặp mới (-9), goto (-10)
mặc dù không có sự thay đổi trước; Điều đó sẽ có giá 17 byte.

tiềm năng hơn

Với /2%9, tôi có thể lưu 15 byte (chỉ 14 byte với /4%5)
bằng cách đưa dữ liệu nhị phân vào một tệp bvà sau đó lập chỉ mục file(b)[0].
Tôi có muốn điều đó không?

Các ký tự UTF-8 sẽ tốn rất nhiều chi phí cho việc chuyển đổi.

băm

Tôi đã sử dụng ZJLO.ST /2%9 -> 0123.56; nhưng T.ZJLOS /3%7 -> 0.23456là tốt
dài hơn một byte: O.STJLZ %13/2 -> 0.23456
và ba byte nữa :OSTZJ.L %17%12%9 -> 01234.6

Tôi không thể tìm thấy một hàm băm ngắn (tối đa 5 byte) mà không có khoảng cách;
nhưng Dave đã tìm thấy STZJL /4%5 -> 01234, bỏ chữ O khỏi danh sách. wtg!

btw: TIJSL.ZO (%12%8) -> 01234.67chừa chỗ cho Ihình dạng
(và hư cấu A, Mhoặc Yhình dạng). %28%8%84%8, làm tương tự (nhưng với Ethay vì A).


Tốt đẹp; Tôi thích bức tranh kết hợp + phát hiện đường kẻ, và break 2sạch sẽ hơn nhiều so với những gì tôi phải làm trong C! Bạn thể lưu một số byte bằng cách sử dụng array_diff(đặt các dòng đã hoàn thành thành một giá trị cố định thay vì sử dụng unsetsau đó thay thế array_valuesbằng array_diff), nhưng tôi không thể biết từ các tài liệu nếu điều đó làm phẳng các giá trị lặp lại (ví dụ: Array_diff ([1,2, 2,3], [1]) -> [2,2,3] hoặc chỉ [2,3])
Dave

@Dave: array_diffkhông xóa các giá trị trùng lặp; và tôi đã có giá trị cố định (1023); nhưng nó không reindex mảng. Ý tưởng tuyệt vời, nhưng nó sẽ tốn một byte.
Tít

wow đó là một tiếng huýt sáo lớn khi bạn bay qua tôi! Có vẻ như tôi đã có một số bắt kịp để làm!
Dave

Chúc mừng bạn đã đạt 300! Tôi sẽ thực hiện thay đổi được đề xuất mới nhất của bạn (chưa nghĩ đến việc đơn giản hóa kiểm tra xoay vòng mà tôi không cần ở /10mọi nơi), nhưng nếu không thì tôi nghĩ là tôi đã xong. Tôi ngạc nhiên về cách mà PHP và C cạnh tranh trực tiếp hóa ra. Điều này thật thú vị - hy vọng OP chấp nhận câu trả lời của bạn!
Dave

@Dave & Titus - Tôi ước tôi có thể chấp nhận cả hai câu trả lời. Các bạn đã làm một công việc tuyệt vời. Dù sao cũng chúc mừng Titus đã đạt 300. Và tôi nghĩ đó thực sự là 299 vì bạn có một không gian vô dụng sau một if.
Arnauld

16

C, 401 392 383 378 374 351 335 324 320 318 316 305 byte

d[99]={'3Z3Z',983177049,513351321,1016270793,970073931,~0},*L=d+5,V,s;char c;main(x){B:for(c=0;c[++L]|V;V>>=4)c-=(L[c]=*L|(V&15)<<x)>1022;s-=c;if(scanf("%c%1d%d,",&c,&V,&x)>2)for(V=c^79?d[c/4%5]>>24-V*8:27,V=c^73?V/64%4<<8|V/8%8<<4|V%8:V&32?15:4369;;--L)for(c=4;c--;)if(L[c]>>x&V>>c*4&15)goto B;return s;}

Lấy đầu vào được phân tách bằng dấu phẩy trên stdin, trả về điểm số trong trạng thái thoát.

Yêu cầu charphải được ký (là mặc định cho GCC) và yêu cầu '3Z3Z'phải được hiểu là 861549402 (ít nhất là trường hợp của GCC trên các máy endian nhỏ).


Ví dụ sử dụng:

echo "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" | ./tetris; echo "$?";

Giải thích cấp cao:

Tất cả các hình dạng ngoại trừ đường có thể nằm gọn trong lưới 3x3 với một góc bị thiếu:

6 7 -
3 4 5
0 1 2

Điều đó có nghĩa là thật dễ dàng để lưu trữ chúng trong một byte mỗi. Ví dụ:

- - -         0 0 -
- # -   -->   0 1 0   -->   0b00010111
# # #         1 1 1

(chúng tôi căn chỉnh từng mảnh ở phía dưới bên trái của hộp để làm cho việc thả nó dễ dàng hơn)

Vì chúng tôi nhận được ít nhất 4 byte cho một int, điều này có nghĩa là chúng tôi có thể lưu trữ tất cả 4 phép quay của mỗi phần trong một số nguyên duy nhất, với trường hợp đặc biệt cho dòng. Chúng ta cũng có thể ghép từng hàng của lưới trò chơi vào một int (chỉ cần 10 bit) và phần hiện đang rơi thành một đoạn dài (4 dòng = 40 bit).


Phá vỡ:

d[99]={             // General memory
  '3Z3Z',           //  Shape of "S" piece (multi-char literal, value = 861549402)
  983177049,        //  Shape of "T" piece
  513351321,        //  Shape of "Z" piece
  1016270793,       //  Shape of "J" piece
  970073931,        //  Shape of "L" piece
  ~0                //  Bottom of game grid (solid row)
},                  //  Rest of array stores lines in the game
*L=d+5,             // Working line pointer (start >= base of grid)
V,                  // Current piece after expansion (also stores rotation)
s;                  // Current score (global to initialise as 0)
char c;             // Input shape identifier (also counts deleted lines)
main(x){            // "x" used to store x location
  B:                                // Loop label; jumps here when piece hits floor
  for(c=0;c[++L]|V;V>>=4)           // Paint latest piece & delete completed lines
    c-=(L[c]=*L|(V&15)<<x)>1022;
  s-=c;                             // Increment score
  if(scanf("%c%1d%d,",&c,&V,&x)>2)  // Load next command
    for(V=c^79?                     // Identify piece & rotation
          d[c/4%5]>>24-V*8          //  Not "O": load from data
        :27,                        //  Else "O": always 27
        V=c^73?                     // If not "I" (line):
          V/64%4<<8|V/8%8<<4|V%8    //  expand shape to 16-bits
        :                           // Else we are a line:
          V&32?                     //  "I" coincides with "J"; use this to check
               15:4369;             //  horizontal/vertical
        ;--L)                       // Loop from top-to-bottom of grid
      for(c=4;c--;)                 //  Loop through rows of piece
        if(L[c]>>x&V>>c*4&15)       //   If collides with existing piece:
          goto B;                   //    Exit loops
  return s;                         // Return score in exit status
}

-4, -1 cảm ơn @Titus và -23, -11 với cảm hứng từ câu trả lời của họ


Đẹp quá Bạn có thể làm s+=(d[A-x]=d[A])mà không cần sử dụng x?
Arnauld

Thật không may xlà cần thiết để theo dõi có bao nhiêu hàng sẽ sụp đổ trong bước hiện tại (mỗi hàng Ađược đặt thành giá trị của hàng A-xkhi vòng lặp tiến triển)
Dave

Ôi! Lỗi của tôi. Xin lỗi cho một đề nghị ngớ ngẩn như vậy. :)
Arnauld

1
@Titus đó là lạm dụng lập chỉ mục mảng của C. Nói một cách đơn giản, 1[a]a[1]làm điều tương tự (hoặc chính xác hơn, a[b]dịch sang *(a+b)). Nó bị lạm dụng như thế này như một cách để tránh dấu ngoặc. Trong trường hợp này, 1[*v]== (*v)[1], tức là chữ cái thứ hai của lệnh, tức là phép quay.
Dave

1
Bạn có thể thoát khỏi Igiữ chỗ? Nếu vậy, hãy thử /2%9như băm thay vì %12. %12%8nếu không.
Tít

2

Ruby, 474 443 428 379 + 48 = 427 byte

-1 cảm ơn @Titus

Điều này chắc chắn có thể được chơi golf nhiều hơn.

Đọc một cuốn từ điển nhị phân mảnh (xem dưới đây) từ STDIN hoặc một tên tập tin và có một danh sách di chuyển như một cuộc tranh cãi, ví dụ $ cat pieces | ruby script.rb O00,T24,S02,....

q=$*.pop
z=$<.read.unpack('S*')
b=c=g=i=0
x=10
u=1023
r=->n{n&2**x-1>0?n:r[n>>x]}
y=->n{n>0?[n&u]+y[n>>x]:[]}
l=->c,n{z.find{|m|m>>x==c<<2|n.to_i}||l[c,n-2]}
q.scan(/(\w)(\d)(\d)/){n=l["IOTLJSZ".index($1),$2.to_i]
v=(n&1|(n&6)<<9|(n&56)<<17|(n&960)<<24)<<$3.to_i
(y[b].size+1).times{t=b<<40
t&v>0&&break
g=t|v
v<<=x}
b=0
a=y[r[g]]
a.grep_v(u){|o|b|=o<<x*i
i+=1}
c+=a.count u}
p c

Dữ liệu mảnh nhị phân (định dạng xxd)

0000000: c003 4b04 d810 d814 b820 9c24 d029 5a2c  ..K...... .$.)Z,
0000010: 7830 9634 e039 ca3c 3841 d444 c849 4e4c  x0.4.9.<8A.D.INL
0000020: 9861 9869 5c64 5c6c f050 f058 9a54 9a5c  .a.i\d\l.P.X.T.\

Xem nó trên repl.it (với các đối số được mã hóa cứng, từ điển): https://repl.it/Cqft/2

Ung dung & giải thích

# Move list from ARGV
q = $*.pop

# Read piece dictionary; pieces are 16-bit integers: 3 for letter,
# 2 for rotation, 10 for shape (and 1 wasted)
z = $<.read.unpack('S*')

# Empty board and various counters
b = c = g = i = 0
x = 10 # Magic numbers
u = 1023

# A function to remove empty lines
r = ->n{ n & 2**x - 1 > 0 ? n : r[n >> x] }

# A function to split the board into lines
y = ->n{ n > 0 ? [n & u] + y[n >> x] : [] }

# A function to lookup a piece by letter and rotation index
l = ->c,n{ z.find {|m| m >> x == c << 2 | n.to_i } || l[c, n-2] }

# Read the move list
q.scan(/(\w)(\d)(\d)/) {
  # Look up the piece
  n = l["IOTLJSZ".index($1), $2.to_i]

  # Convert the 10-bit piece to a 40-bit piece (4 rows of 10 columns)
  v = (n & 1 |
        (n & 6) << 9 |
        (n & 56) << 17 |
        (n & 960) << 24
      ) << $3.to_i # Shift by the appropriate number of columns

  # Drop the piece onto the board
  (y[b].size + 1).times {
    t = b << 40
    t & v > 0 && break
    g = t | v
    v <<= x
  }

  # Clear completed rows
  b = 0
  a = y[r[g]]
  a.grep_v(u) {|o|
    b |= o << x * i
    i += 1
  }

  c += a.count u # Count cleared rows
}
p c

1 byte: m >> 10có thể làm >> x
Titus

@Titus Mắt tốt. Cảm ơn!
Jordan

Không cần rõ ràng yêu cầu \ds trong biểu thức chính quy: /(\w)(\d)(\d)//(\w)(.)(.)/
manatwork

2

PHP, 454 435 427 420 414 byte

trường bit cho các mảnh và bản đồ; nhưng không có trường hợp đặc biệt nào cho Ihình dạng như chơi gôn Dave Daves.

<?$t=[I=>[15,4369],O=>[51],T=>[114,562,39,305],L=>[113,802,71,275],J=>[116,547,23,785],Z=>[54,561],S=>[99,306]];foreach($argv as$z=>$m)if($z){$s=$t[$m[0]][$m[1]%count($t[$m[0]])];for($d=$i=0;$i<4;$i++)for($k=0;$k<4;$k++)if($s>>4*$k&1<<$i){for($y=0;$y++<count($f);)if($f[$y-1]&1<<$m[2]+$i)$d=max($d,$y-$k);$k=3;}for($k=$d;$s;$k++,$s>>=4)if(1022<$f[$k]|=$s%16<<$m[2]){$c++;unset($f[$k]);}$f=array_values($f);}echo$c;

lấy đối số từ dòng lệnh, in kết quả

không có chức năng

lấy đối số làm mảng, trả về kết quả

function t($a)
{
    // bitwise description of the stones and rotations
    $t=[I=>[15,4369],O=>[51],T=>[114,562,39,305],L=>[113,802,71,275],J=>[116,547,23,785],Z=>[54,561],S=>[99,306]];
    foreach($a as$m)
    {
        $s=$t[$m[0]][$m[1]%count($t[$m[0]])];   // $s=stone
        // find dropping space
        for($d=$i=0;$i<4;$i++)
            // a) lowest pixel of stone in column i
            for($k=0;$k<4;$k++)
                if($s>>4*$k&1<<$i)
                {
                    // b) top pixel of field in column x+i 
                    for($y=0;$y++<count($f);)
                        if($f[$y-1]&1<<$m[2]+$i)$d=max($d,$y-$k);
                    $k=3; // one byte shorter than `break;`
                }
        // do drop
        for($k=$d;$s;$k++,$s>>=4)
            if(1022<$f[$k]|=$s%16<<$m[2])   // add block pixels to line pixels ... if full,
            {$c++;unset($f[$k]);}           // tetris
        $f=array_values($f);
    }
    return$c;
}

kiểm tra (về chức năng)

$data=[
    "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19"=>4,
    "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" => 5,
    "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" => 4,
    "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,I10,I10" => 8,
    // additional example for the two last tetrominoes:
    'O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02' => 8,
];
function out($a){if(is_object($a)){foreach($a as$v)$r[]=$v;return'{'.implode(',',$r).'}';}if(!is_array($a))return$a;$r=[];foreach($a as$v)$r[]=out($v);return'['.join(',',$r).']';}
function cmp($a,$b){if(is_numeric($a)&&is_numeric($b))return 1e-2<abs($a-$b);if(is_array($a)&&is_array($b)&&count($a)==count($b)){foreach($a as $v){$w = array_shift($b);if(cmp($v,$w))return true;}return false;}return strcmp($a,$b);}
function test($x,$e,$y){static $h='<table border=1><tr><th>input</th><th>output</th><th>expected</th><th>ok?</th></tr>';echo"$h<tr><td>",out($x),'</td><td>',out($y),'</td><td>',out($e),'</td><td>',cmp($e,$y)?'N':'Y',"</td></tr>";$h='';}
foreach($data as $v=>$e)
{
    $x=explode(',',$v);
    test($x,$e,t($x));
}

427? Bạn đang ở trên!
Jordan

@Jordan: và những người ở đây bao gồm cả <?chi phí hoạt động :)
Tít
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.